From fbab5db85adaa6055539fd4644fe210a4211031e Mon Sep 17 00:00:00 2001 From: Dessalines Date: Mon, 10 Feb 2020 14:45:50 -0500 Subject: [PATCH 001/164] Fixing ansible certbot renew. --- ansible/lemmy.yml | 2 +- ansible/lemmy_dev.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ansible/lemmy.yml b/ansible/lemmy.yml index 87f71699d..c415abef5 100644 --- a/ansible/lemmy.yml +++ b/ansible/lemmy.yml @@ -63,4 +63,4 @@ special_time=daily name=certbot-renew-lemmy user=root - job="certbot certonly --nginx -d '{{ domain }}' --deploy-hook 'docker-compose -f /peertube/docker-compose.yml exec nginx nginx -s reload'" + job="certbot certonly --nginx -d '{{ domain }}' --deploy-hook 'nginx -s reload'" diff --git a/ansible/lemmy_dev.yml b/ansible/lemmy_dev.yml index c15569faf..c150714ca 100644 --- a/ansible/lemmy_dev.yml +++ b/ansible/lemmy_dev.yml @@ -97,4 +97,4 @@ special_time=daily name=certbot-renew-lemmy user=root - job="certbot certonly --nginx -d '{{ domain }}' --deploy-hook 'docker-compose -f /peertube/docker-compose.yml exec nginx nginx -s reload'" + job="certbot certonly --nginx -d '{{ domain }}' --deploy-hook 'nginx -s reload'" From 774518e4fe336ac60c6e8ccdbc9a98e0cb8b9310 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Tue, 11 Feb 2020 10:14:09 -0500 Subject: [PATCH 002/164] Fixing unread indicator on link click. Fixes #527 --- ui/src/components/inbox.tsx | 2 +- ui/src/components/navbar.tsx | 8 +++++--- ui/src/components/post.tsx | 4 ++++ ui/src/interfaces.ts | 1 + ui/src/services/UserService.ts | 7 +++---- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/ui/src/components/inbox.tsx b/ui/src/components/inbox.tsx index 027a1db04..56bf15785 100644 --- a/ui/src/components/inbox.tsx +++ b/ui/src/components/inbox.tsx @@ -443,9 +443,9 @@ export class Inbox extends Component { this.state.messages.filter( r => !r.read && r.creator_id !== UserService.Instance.user.id ).length; + UserService.Instance.user.unreadCount = count; UserService.Instance.sub.next({ user: UserService.Instance.user, - unreadCount: count, }); } } diff --git a/ui/src/components/navbar.tsx b/ui/src/components/navbar.tsx index c675cfe7b..75cdd5549 100644 --- a/ui/src/components/navbar.tsx +++ b/ui/src/components/navbar.tsx @@ -60,8 +60,10 @@ export class Navbar extends Component { // Subscribe to user changes this.userSub = UserService.Instance.sub.subscribe(user => { this.state.isLoggedIn = user.user !== undefined; - this.state.unreadCount = user.unreadCount; - this.requestNotificationPermission(); + if (this.state.isLoggedIn) { + this.state.unreadCount = user.user.unreadCount; + this.requestNotificationPermission(); + } this.setState(this.state); }); @@ -304,9 +306,9 @@ export class Navbar extends Component { } sendUnreadCount() { + UserService.Instance.user.unreadCount = this.state.unreadCount; UserService.Instance.sub.next({ user: UserService.Instance.user, - unreadCount: this.state.unreadCount, }); } diff --git a/ui/src/components/post.tsx b/ui/src/components/post.tsx index b5b1fce36..d8f662cf7 100644 --- a/ui/src/components/post.tsx +++ b/ui/src/components/post.tsx @@ -156,6 +156,10 @@ export class Post extends Component { auth: null, }; WebSocketService.Instance.editComment(form); + UserService.Instance.user.unreadCount--; + UserService.Instance.sub.next({ + user: UserService.Instance.user, + }); } } diff --git a/ui/src/interfaces.ts b/ui/src/interfaces.ts index 23551b595..5846b548c 100644 --- a/ui/src/interfaces.ts +++ b/ui/src/interfaces.ts @@ -93,6 +93,7 @@ export interface User { lang: string; avatar?: string; show_avatars: boolean; + unreadCount?: number; } export interface UserView { diff --git a/ui/src/services/UserService.ts b/ui/src/services/UserService.ts index 03380e593..47e28c73e 100644 --- a/ui/src/services/UserService.ts +++ b/ui/src/services/UserService.ts @@ -7,9 +7,8 @@ import { Subject } from 'rxjs'; export class UserService { private static _instance: UserService; public user: User; - public sub: Subject<{ user: User; unreadCount: number }> = new Subject<{ + public sub: Subject<{ user: User }> = new Subject<{ user: User; - unreadCount: number; }>(); private constructor() { @@ -32,7 +31,7 @@ export class UserService { this.user = undefined; Cookies.remove('jwt'); setTheme(); - this.sub.next({ user: undefined, unreadCount: 0 }); + this.sub.next({ user: undefined }); console.log('Logged out.'); } @@ -45,7 +44,7 @@ export class UserService { if (this.user.theme != 'darkly') { setTheme(this.user.theme); } - this.sub.next({ user: this.user, unreadCount: 0 }); + this.sub.next({ user: this.user }); console.log(this.user); } From 0b69974e49747f63d116e28d6b0ad5043452cab4 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 12 Feb 2020 12:12:19 -0500 Subject: [PATCH 003/164] Add community refine by searching on new post creation. Fixes #521 --- ui/assets/css/main.css | 6 ++++++ ui/assets/css/selectr.min.css | 7 +++++++ ui/package.json | 1 + ui/src/components/post-form.tsx | 8 ++++++++ ui/src/index.html | 1 + ui/yarn.lock | 5 +++++ 6 files changed, 28 insertions(+) create mode 100644 ui/assets/css/selectr.min.css diff --git a/ui/assets/css/main.css b/ui/assets/css/main.css index df75ec319..e7784877b 100644 --- a/ui/assets/css/main.css +++ b/ui/assets/css/main.css @@ -181,3 +181,9 @@ hr { -webkit-transform: scale(1.2); -ms-transform: scale(1.2); } + +.selectr-selected, .selectr-options-container { + background-color: var(--secondary); + color: var(--white); + border: unset; +} diff --git a/ui/assets/css/selectr.min.css b/ui/assets/css/selectr.min.css new file mode 100644 index 000000000..78bab83fc --- /dev/null +++ b/ui/assets/css/selectr.min.css @@ -0,0 +1,7 @@ +/*! + * Selectr 2.4.13 + * http://mobius.ovh/docs/selectr + * + * Released under the MIT license + */ +.selectr-container li,.selectr-option,.selectr-tag{list-style:none}.selectr-container{position:relative}.selectr-hidden{position:absolute;overflow:hidden;clip:rect(0,0,0,0);width:1px;height:1px;margin:-1px;padding:0;border:0}.selectr-visible{position:absolute;left:0;top:0;width:100%;height:100%;opacity:0;z-index:11}.selectr-desktop.multiple .selectr-visible{display:none}.selectr-desktop.multiple.native-open .selectr-visible{top:100%;min-height:200px!important;height:auto;opacity:1;display:block}.selectr-container.multiple.selectr-mobile .selectr-selected{z-index:0}.selectr-selected{position:relative;z-index:1;box-sizing:border-box;width:100%;padding:7px 28px 7px 14px;cursor:pointer;border:1px solid #999;border-radius:3px;}.selectr-selected::before{position:absolute;top:50%;right:10px;width:0;height:0;content:'';-o-transform:rotate(0) translate3d(0,-50%,0);-ms-transform:rotate(0) translate3d(0,-50%,0);-moz-transform:rotate(0) translate3d(0,-50%,0);-webkit-transform:rotate(0) translate3d(0,-50%,0);transform:rotate(0) translate3d(0,-50%,0);border-width:4px 4px 0;border-style:solid;border-color:#6c7a86 transparent transparent}.selectr-container.native-open .selectr-selected::before,.selectr-container.open .selectr-selected::before{border-width:0 4px 4px;border-style:solid;border-color:transparent transparent #6c7a86}.selectr-label{display:none;overflow:hidden;width:100%;white-space:nowrap;text-overflow:ellipsis}.selectr-placeholder{color:#6c7a86}.selectr-tags{margin:0;padding:0;white-space:normal}.has-selected .selectr-tags{margin:0 0 -2px}.selectr-tag{position:relative;float:left;padding:2px 25px 2px 8px;margin:0 2px 2px 0;cursor:default;color:#fff;border:none;border-radius:10px;background:#acb7bf}.selectr-container.multiple.has-selected .selectr-selected{padding:5px 28px 5px 5px}.selectr-options-container{position:absolute;z-index:10000;top:calc(100% - 1px);left:0;display:none;box-sizing:border-box;width:100%;border-width:0 1px 1px;border-style:solid;border-color:transparent #999 #999;border-radius:0 0 3px 3px;}.selectr-container.open .selectr-options-container{display:block}.selectr-input-container{position:relative;display:none}.selectr-clear,.selectr-input-clear,.selectr-tag-remove{position:absolute;top:50%;right:22px;width:20px;height:20px;padding:0;cursor:pointer;-o-transform:translate3d(0,-50%,0);-ms-transform:translate3d(0,-50%,0);-moz-transform:translate3d(0,-50%,0);-webkit-transform:translate3d(0,-50%,0);transform:translate3d(0,-50%,0);border:none;background-color:transparent;z-index:11}.selectr-clear,.selectr-input-clear{display:none}.selectr-container.has-selected .selectr-clear,.selectr-input-container.active,.selectr-input-container.active .selectr-clear,.selectr-input-container.active .selectr-input-clear{display:block}.selectr-selected .selectr-tag-remove{right:2px}.selectr-clear::after,.selectr-clear::before,.selectr-input-clear::after,.selectr-input-clear::before,.selectr-tag-remove::after,.selectr-tag-remove::before{position:absolute;top:5px;left:9px;width:2px;height:10px;content:' ';background-color:#6c7a86}.selectr-tag-remove::after,.selectr-tag-remove::before{top:4px;width:3px;height:12px;}.selectr-clear:before,.selectr-input-clear::before,.selectr-tag-remove::before{-o-transform:rotate(45deg);-ms-transform:rotate(45deg);-moz-transform:rotate(45deg);-webkit-transform:rotate(45deg);transform:rotate(45deg)}.selectr-clear:after,.selectr-input-clear::after,.selectr-tag-remove::after{-o-transform:rotate(-45deg);-ms-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}.selectr-input{top:5px;left:5px;box-sizing:border-box;width:calc(100% - 30px);margin:10px 15px;padding:7px 30px 7px 9px;border:1px solid #999;border-radius:3px}.selectr-notice{display:none;box-sizing:border-box;width:100%;padding:8px 16px;border-top:1px solid #999;border-radius:0 0 3px 3px;}.input-tag,.taggable .selectr-label{width:auto}.selectr-container.notice .selectr-notice{display:block}.selectr-container.notice .selectr-selected{border-radius:3px 3px 0 0}.selectr-options{position:relative;top:calc(100% + 2px);display:none;overflow-x:auto;overflow-y:scroll;max-height:200px;margin:0;padding:0}.selectr-container.notice .selectr-options-container,.selectr-container.open .selectr-input-container,.selectr-container.open .selectr-options{display:block}.selectr-option{position:relative;display:block;padding:5px 20px;cursor:pointer;font-weight:400}.has-selected .selectr-placeholder,.selectr-empty,.selectr-option.excluded{display:none}.selectr-options.optgroups>.selectr-option{padding-left:25px}.selectr-optgroup{font-weight:700;padding:0}.selectr-optgroup--label{font-weight:700;margin-top:10px;padding:5px 15px}.selectr-match{text-decoration:underline}.selectr-option.selected{background-color:#ddd}.selectr-option.active{color:#fff;background-color:#5897fb}.selectr-option.disabled{opacity:.4}.selectr-container.open .selectr-selected{border-color:#999 #999 transparent;border-radius:3px 3px 0 0}.selectr-container.open .selectr-selected::after{-o-transform:rotate(180deg) translate3d(0,50%,0);-ms-transform:rotate(180deg) translate3d(0,50%,0);-moz-transform:rotate(180deg) translate3d(0,50%,0);-webkit-transform:rotate(180deg) translate3d(0,50%,0);transform:rotate(180deg) translate3d(0,50%,0)}.selectr-disabled{opacity:.6}.has-selected .selectr-label{display:block}.taggable .selectr-selected{padding:4px 28px 4px 4px}.taggable .selectr-selected::after{display:table;content:" ";clear:both}.taggable .selectr-tags{float:left;display:block}.taggable .selectr-placeholder{display:none}.input-tag{float:left;min-width:90px}.selectr-tag-input{border:none;padding:3px 10px;width:100%;font-family:inherit;font-weight:inherit;font-size:inherit}.selectr-input-container.loading::after{position:absolute;top:50%;right:20px;width:20px;height:20px;content:'';-o-transform:translate3d(0,-50%,0);-ms-transform:translate3d(0,-50%,0);-moz-transform:translate3d(0,-50%,0);-webkit-transform:translate3d(0,-50%,0);transform:translate3d(0,-50%,0);-o-transform-origin:50% 0 0;-ms-transform-origin:50% 0 0;-moz-transform-origin:50% 0 0;-webkit-transform-origin:50% 0 0;transform-origin:50% 0 0;-moz-animation:.5s linear 0s normal forwards infinite running spin;-webkit-animation:.5s linear 0s normal forwards infinite running spin;animation:.5s linear 0s normal forwards infinite running spin;border-width:3px;border-style:solid;border-color:#aaa #ddd #ddd;border-radius:50%}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0) translate3d(0,-50%,0);transform:rotate(0) translate3d(0,-50%,0)}100%{-webkit-transform:rotate(360deg) translate3d(0,-50%,0);transform:rotate(360deg) translate3d(0,-50%,0)}}@keyframes spin{0%{-webkit-transform:rotate(0) translate3d(0,-50%,0);transform:rotate(0) translate3d(0,-50%,0)}100%{-webkit-transform:rotate(360deg) translate3d(0,-50%,0);transform:rotate(360deg) translate3d(0,-50%,0)}}.selectr-container.open.inverted .selectr-selected{border-color:transparent #999 #999;border-radius:0 0 3px 3px}.selectr-container.inverted .selectr-options-container{border-width:1px 1px 0;border-color:#999 #999 transparent;border-radius:3px 3px 0 0;top:auto;bottom:calc(100% - 1px)}.selectr-container ::-webkit-input-placeholder{color:#6c7a86;opacity:1}.selectr-container ::-moz-placeholder{color:#6c7a86;opacity:1}.selectr-container :-ms-input-placeholder{color:#6c7a86;opacity:1}.selectr-container ::placeholder{color:#6c7a86;opacity:1} diff --git a/ui/package.json b/ui/package.json index 9cf349108..6d7ad7758 100644 --- a/ui/package.json +++ b/ui/package.json @@ -33,6 +33,7 @@ "markdown-it": "^10.0.0", "markdown-it-container": "^2.0.0", "markdown-it-emoji": "^1.4.0", + "mobius1-selectr": "^2.4.13", "moment": "^2.24.0", "prettier": "^1.18.2", "reconnecting-websocket": "^4.4.0", diff --git a/ui/src/components/post-form.tsx b/ui/src/components/post-form.tsx index 7984c2a85..8abdf26d1 100644 --- a/ui/src/components/post-form.tsx +++ b/ui/src/components/post-form.tsx @@ -35,6 +35,7 @@ import { } from '../utils'; import autosize from 'autosize'; import Tribute from 'tributejs/src/Tribute.js'; +import Selectr from 'mobius1-selectr'; import { i18n } from '../i18next'; const MAX_POST_TITLE_LENGTH = 200; @@ -514,6 +515,13 @@ export class PostForm extends Component { this.state.postForm.community_id = data.communities[0].id; } this.setState(this.state); + + // Set up select searching + let selectId: any = document.getElementById('post-community'); + let selector = new Selectr(selectId, {}); + selector.on('selectr.select', option => { + this.state.postForm.community_id = Number(option.value); + }); } else if (res.op == UserOperation.CreatePost) { let data = res.data as PostResponse; if (data.post.creator_id == UserService.Instance.user.id) { diff --git a/ui/src/index.html b/ui/src/index.html index 09f13097d..24bed7247 100644 --- a/ui/src/index.html +++ b/ui/src/index.html @@ -13,6 +13,7 @@ + diff --git a/ui/yarn.lock b/ui/yarn.lock index 5c9ad637a..4cbc90d95 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -3076,6 +3076,11 @@ mkdirp@^0.5.1: dependencies: minimist "0.0.8" +mobius1-selectr@^2.4.13: + version "2.4.13" + resolved "https://registry.yarnpkg.com/mobius1-selectr/-/mobius1-selectr-2.4.13.tgz#0019dfd9f984840d6e40f70683ab3ec78ce3b5df" + integrity sha512-Mk9qDrvU44UUL0EBhbAA1phfQZ7aMZPjwtL7wkpiBzGh8dETGqfsh50mWoX9EkjDlkONlErWXArHCKfoxVg0Bw== + moment@^2.24.0: version "2.24.0" resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" From a27badefba3bdc46bd322358da5b2fd001b9e5cc Mon Sep 17 00:00:00 2001 From: Dessalines Date: Thu, 13 Feb 2020 12:35:22 -0500 Subject: [PATCH 004/164] Fix minor loading bug. --- ui/src/utils.ts | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/ui/src/utils.ts b/ui/src/utils.ts index 384d5c1d5..329168d26 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -613,11 +613,13 @@ export function createPostLikeFindRes(data: PostResponse, posts: Array) { } export function createPostLikeRes(data: PostResponse, post: Post) { - post.score = data.post.score; - post.upvotes = data.post.upvotes; - post.downvotes = data.post.downvotes; - if (data.post.my_vote !== null) { - post.my_vote = data.post.my_vote; + if (post) { + post.score = data.post.score; + post.upvotes = data.post.upvotes; + post.downvotes = data.post.downvotes; + if (data.post.my_vote !== null) { + post.my_vote = data.post.my_vote; + } } } @@ -629,9 +631,11 @@ export function editPostFindRes(data: PostResponse, posts: Array) { } export function editPostRes(data: PostResponse, post: Post) { - post.url = data.post.url; - post.name = data.post.name; - post.nsfw = data.post.nsfw; + if (post) { + post.url = data.post.url; + post.name = data.post.name; + post.nsfw = data.post.nsfw; + } } export function commentsToFlatNodes( From 177c462b40edd2e9eb7da0d1052f4a82f492dfce Mon Sep 17 00:00:00 2001 From: Dessalines Date: Fri, 14 Feb 2020 10:02:37 -0500 Subject: [PATCH 005/164] Making links go to post page. --- ui/src/components/post-listing.tsx | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index 1e7289013..4dea4d86c 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -171,24 +171,13 @@ export class PostListing extends Component {
- {post.url ? ( - - {post.name} - - ) : ( - - {post.name} - - )} + + {post.name} +
{post.url && ( From 91649d21ae69aa649f9ba016d7d662d5c4c0b187 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Fri, 14 Feb 2020 10:12:40 -0500 Subject: [PATCH 006/164] Version v0.6.18 --- ansible/VERSION | 2 +- docker/prod/docker-compose.yml | 2 +- server/src/version.rs | 2 +- ui/src/version.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ansible/VERSION b/ansible/VERSION index c32d86a50..5425cbcce 100644 --- a/ansible/VERSION +++ b/ansible/VERSION @@ -1 +1 @@ -v0.6.17 +v0.6.18 diff --git a/docker/prod/docker-compose.yml b/docker/prod/docker-compose.yml index 4baceb3ed..876c82c14 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.17 + image: dessalines/lemmy:v0.6.18 ports: - "127.0.0.1:8536:8536" restart: always diff --git a/server/src/version.rs b/server/src/version.rs index 92eb4e7f3..6ec29de8e 100644 --- a/server/src/version.rs +++ b/server/src/version.rs @@ -1 +1 @@ -pub const VERSION: &str = "v0.6.17"; +pub const VERSION: &str = "v0.6.18"; diff --git a/ui/src/version.ts b/ui/src/version.ts index 8eab99577..262956349 100644 --- a/ui/src/version.ts +++ b/ui/src/version.ts @@ -1 +1 @@ -export const version: string = 'v0.6.17'; +export const version: string = 'v0.6.18'; From ed07e98bffd3149a74b5a2584a50bcbc7b610caf Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 14 Feb 2020 21:30:06 +0100 Subject: [PATCH 007/164] changed permissions from db-init.sh to be able to run --- server/db-init.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 server/db-init.sh diff --git a/server/db-init.sh b/server/db-init.sh old mode 100644 new mode 100755 From 845c2b70a5edba71bef4bd6be48f71a214241c17 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Fri, 14 Feb 2020 22:28:29 -0500 Subject: [PATCH 008/164] Adding an external link icon. #530 --- ui/src/components/post-listing.tsx | 3 +++ ui/src/components/symbols.tsx | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index 4dea4d86c..1b8042f8a 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -188,6 +188,9 @@ export class PostListing extends Component { title={post.url} > {new URL(post.url).hostname} + + + )} diff --git a/ui/src/components/symbols.tsx b/ui/src/components/symbols.tsx index 829201e4c..0870efd71 100644 --- a/ui/src/components/symbols.tsx +++ b/ui/src/components/symbols.tsx @@ -15,6 +15,10 @@ export class Symbols extends Component { xmlnsXlink="http://www.w3.org/1999/xlink" > + + external-link + + coffee1 From 83d17b2643f1c9d56f2338cb244d857eddaf1fb4 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Sat, 15 Feb 2020 16:09:53 -0500 Subject: [PATCH 009/164] Fix minor issue with selector. Fix issue with truncate wrapping. --- ui/src/components/post-form.tsx | 10 ++++++---- ui/src/components/post-listing.tsx | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/ui/src/components/post-form.tsx b/ui/src/components/post-form.tsx index 8abdf26d1..d502330b4 100644 --- a/ui/src/components/post-form.tsx +++ b/ui/src/components/post-form.tsx @@ -518,10 +518,12 @@ export class PostForm extends Component { // Set up select searching let selectId: any = document.getElementById('post-community'); - let selector = new Selectr(selectId, {}); - selector.on('selectr.select', option => { - this.state.postForm.community_id = Number(option.value); - }); + if (selectId) { + let selector = new Selectr(selectId, { nativeDropdown: false }); + selector.on('selectr.select', option => { + this.state.postForm.community_id = Number(option.value); + }); + } } else if (res.op == UserOperation.CreatePost) { let data = res.data as PostResponse; if (data.post.creator_id == UserService.Instance.user.id) { diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index 1b8042f8a..41cd6675a 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -169,7 +169,7 @@ export class PostListing extends Component { )}
-
+
{
{post.url && ( - + Date: Sat, 15 Feb 2020 20:29:57 -0500 Subject: [PATCH 010/164] Some front end fixes. --- ui/src/components/community.tsx | 6 +++--- ui/src/components/main.tsx | 4 ++-- ui/src/components/post-listing.tsx | 25 ++++++++++++++++++------- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/ui/src/components/community.tsx b/ui/src/components/community.tsx index e28c99bc7..67386469e 100644 --- a/ui/src/components/community.tsx +++ b/ui/src/components/community.tsx @@ -136,6 +136,7 @@ export class Community extends Component { render() { return (
+ {this.selects()} {this.state.loading ? (
@@ -158,7 +159,6 @@ export class Community extends Component { )}
- {this.selects()} {this.listings()} {this.paginator()}
@@ -200,7 +200,7 @@ export class Community extends Component { onChange={this.handleDataTypeChange} /> - +
{ }`} target="_blank" > - + # diff --git a/ui/src/components/main.tsx b/ui/src/components/main.tsx index c8e132f7a..87a2fb66b 100644 --- a/ui/src/components/main.tsx +++ b/ui/src/components/main.tsx @@ -386,6 +386,7 @@ export class Main extends Component { posts() { return (
+ {this.selects()} {this.state.loading ? (
@@ -394,7 +395,6 @@ export class Main extends Component {
) : (
- {this.selects()} {this.listings()} {this.paginator()}
@@ -428,7 +428,7 @@ export class Main extends Component { type_={this.state.dataType} onChange={this.handleDataTypeChange} /> - + {
- - {post.name} - + {this.props.showBody && post.url ? ( + + {post.name} + + ) : ( + + {post.name} + + )}
{post.url && ( From b6ce26023efc75ca2b871d12c4f1c2c7b9696083 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Sun, 16 Feb 2020 14:26:39 -0500 Subject: [PATCH 011/164] Version v0.6.19 --- ansible/VERSION | 2 +- docker/prod/docker-compose.yml | 2 +- server/src/version.rs | 2 +- ui/src/version.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ansible/VERSION b/ansible/VERSION index 5425cbcce..6139a0061 100644 --- a/ansible/VERSION +++ b/ansible/VERSION @@ -1 +1 @@ -v0.6.18 +v0.6.19 diff --git a/docker/prod/docker-compose.yml b/docker/prod/docker-compose.yml index 876c82c14..3472be5d6 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.18 + image: dessalines/lemmy:v0.6.19 ports: - "127.0.0.1:8536:8536" restart: always diff --git a/server/src/version.rs b/server/src/version.rs index 6ec29de8e..cd7310c47 100644 --- a/server/src/version.rs +++ b/server/src/version.rs @@ -1 +1 @@ -pub const VERSION: &str = "v0.6.18"; +pub const VERSION: &str = "v0.6.19"; diff --git a/ui/src/version.ts b/ui/src/version.ts index 262956349..c58b46d3f 100644 --- a/ui/src/version.ts +++ b/ui/src/version.ts @@ -1 +1 @@ -export const version: string = 'v0.6.18'; +export const version: string = 'v0.6.19'; From ebff00e0571ca4ac7f0a1a6fbd1e751211656ad4 Mon Sep 17 00:00:00 2001 From: Andre Vallestero Date: Sun, 16 Feb 2020 16:04:00 -0500 Subject: [PATCH 012/164] Added french translation for sponsor message --- ui/src/translations/fr.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui/src/translations/fr.ts b/ui/src/translations/fr.ts index 663b7dcb9..f16af7bfc 100644 --- a/ui/src/translations/fr.ts +++ b/ui/src/translations/fr.ts @@ -140,8 +140,7 @@ export const fr = { "Lemmy est gratuit et <1>open-source, c'est à dire sans publicité et sans monétisation. Pour toujours. Vos dons soutiennent directement le développement du projet. Merci à nos soutiens.", support_on_patreon: 'Soutenir sur Patreon', support_on_liberapay: 'Soutenir sur Liberapay', - general_sponsors: - 'General Sponsors are those that pledged $10 to $39 to Lemmy.', + general_sponsors: 'Les sponsors généraux sont ceux garantissant de 10 à 39$.', crypto: 'Cryptomonnaies', bitcoin: 'Bitcoin', ethereum: 'Ethereum', From 55f91ac5dc1816463fb99d6974f89acd46de3444 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Mon, 17 Feb 2020 11:18:01 -0500 Subject: [PATCH 013/164] First pass at adding oembeds / iframely. --- ansible/lemmy.yml | 1 + ansible/lemmy_dev.yml | 1 + ansible/templates/docker-compose.yml | 9 + ansible/templates/nginx.conf | 7 + docker/dev/docker-compose.yml | 8 + docker/iframely.config.local.js | 283 ++++++++++++++++++++++ docker/prod/docker-compose.yml | 8 + docs/src/administration_install_docker.md | 1 + ui/src/components/iframely-card.tsx | 100 ++++++++ ui/src/components/post-listing.tsx | 53 +++- ui/src/interfaces.ts | 15 ++ 11 files changed, 482 insertions(+), 4 deletions(-) create mode 100644 docker/iframely.config.local.js create mode 100644 ui/src/components/iframely-card.tsx diff --git a/ansible/lemmy.yml b/ansible/lemmy.yml index c415abef5..8d5e22641 100644 --- a/ansible/lemmy.yml +++ b/ansible/lemmy.yml @@ -35,6 +35,7 @@ with_items: - { src: 'templates/docker-compose.yml', dest: '/lemmy/docker-compose.yml', mode: '0600' } - { src: 'templates/nginx.conf', dest: '/etc/nginx/sites-enabled/lemmy.conf', mode: '0644' } + - { src: '../docker/iframely.config.local.js', dest: '/lemmy/iframely.config.local.js', mode: '0600' } - name: add config file (only during initial setup) template: src='templates/config.hjson' dest='/lemmy/lemmy.hjson' mode='0600' force='no' owner='1000' group='1000' diff --git a/ansible/lemmy_dev.yml b/ansible/lemmy_dev.yml index c150714ca..e9b8364f3 100644 --- a/ansible/lemmy_dev.yml +++ b/ansible/lemmy_dev.yml @@ -37,6 +37,7 @@ with_items: - { src: 'templates/docker-compose.yml', dest: '/lemmy/docker-compose.yml', mode: '0600' } - { src: 'templates/nginx.conf', dest: '/etc/nginx/sites-enabled/lemmy.conf', mode: '0644' } + - { src: '../docker/iframely.config.local.js', dest: '/lemmy/iframely.config.local.js', mode: '0600' } - name: add config file (only during initial setup) template: src='templates/config.hjson' dest='/lemmy/lemmy.hjson' mode='0600' force='no' owner='1000' group='1000' diff --git a/ansible/templates/docker-compose.yml b/ansible/templates/docker-compose.yml index 2693d7ad2..bf9aeeb5a 100644 --- a/ansible/templates/docker-compose.yml +++ b/ansible/templates/docker-compose.yml @@ -30,6 +30,14 @@ services: - lemmy_pictshare:/usr/share/nginx/html/data restart: always + lemmy_iframely: + image: dogbin/iframely:latest + ports: + - "127.0.0.1:8061:8061" + volumes: + - ./iframely.config.local.js:/iframely/config.local.js:ro + restart: always + postfix: image: mwader/postfix-relay environment: @@ -38,3 +46,4 @@ services: volumes: lemmy_db: lemmy_pictshare: + lemmy_iframely: diff --git a/ansible/templates/nginx.conf b/ansible/templates/nginx.conf index 9f31140b2..04e5a6436 100644 --- a/ansible/templates/nginx.conf +++ b/ansible/templates/nginx.conf @@ -80,6 +80,13 @@ server { add_header Cache-Control "public, max-age=31536000, immutable"; } } + + location /iframely/ { + proxy_pass http://0.0.0.0:8061/; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } } # Anonymize IP addresses diff --git a/docker/dev/docker-compose.yml b/docker/dev/docker-compose.yml index eabd334d5..987be4d5b 100644 --- a/docker/dev/docker-compose.yml +++ b/docker/dev/docker-compose.yml @@ -28,6 +28,14 @@ services: volumes: - lemmy_pictshare:/usr/share/nginx/html/data restart: always + lemmy_iframely: + image: dogbin/iframely:latest + ports: + - "127.0.0.1:8061:8061" + volumes: + - ../iframely.config.local.js:/iframely/config.local.js:ro + restart: always volumes: lemmy_db: lemmy_pictshare: + lemmy_iframely: diff --git a/docker/iframely.config.local.js b/docker/iframely.config.local.js new file mode 100644 index 000000000..5c00cb143 --- /dev/null +++ b/docker/iframely.config.local.js @@ -0,0 +1,283 @@ +(function() { + var config = { + + // Specify a path for custom plugins. Custom plugins will override core plugins. + // CUSTOM_PLUGINS_PATH: __dirname + '/yourcustom-plugin-folder', + + DEBUG: false, + RICH_LOG_ENABLED: false, + + // For embeds that require render, baseAppUrl will be used as the host. + baseAppUrl: "http://yourdomain.com", + relativeStaticUrl: "/r", + + // Or just skip built-in renders altogether + SKIP_IFRAMELY_RENDERS: true, + + // For legacy reasons the response format of Iframely open-source is + // different by default as it does not group the links array by rel. + // In order to get the same grouped response as in Cloud API, + // add `&group=true` to your request to change response per request + // or set `GROUP_LINKS` in your config to `true` for a global change. + GROUP_LINKS: true, + + // Number of maximum redirects to follow before aborting the page + // request with `redirect loop` error. + MAX_REDIRECTS: 4, + + SKIP_OEMBED_RE_LIST: [ + // /^https?:\/\/yourdomain\.com\//, + ], + + /* + // Used to pass parameters to the generate functions when creating HTML elements + // disableSizeWrapper: Don't wrap element (iframe, video, etc) in a positioned div + GENERATE_LINK_PARAMS: { + disableSizeWrapper: true + }, + */ + + port: 8061, //can be overridden by PORT env var + host: '0.0.0.0', // Dockers beware. See https://github.com/itteco/iframely/issues/132#issuecomment-242991246 + //can be overridden by HOST env var + + // Optional SSL cert, if you serve under HTTPS. + /* + ssl: { + key: require('fs').readFileSync(__dirname + '/key.pem'), + cert: require('fs').readFileSync(__dirname + '/cert.pem'), + port: 443 + }, + */ + + /* + Supported cache engines: + - no-cache - no caching will be used. + - node-cache - good for debug, node memory will be used (https://github.com/tcs-de/nodecache). + - redis - https://github.com/mranney/node_redis. + - memcached - https://github.com/3rd-Eden/node-memcached + */ + CACHE_ENGINE: 'node-cache', + CACHE_TTL: 0, // In seconds. + // 0 = 'never expire' for memcached & node-cache to let cache engine decide itself when to evict the record + // 0 = 'no cache' for redis. Use high enough (e.g. 365*24*60*60*1000) ttl for similar 'never expire' approach instead + + /* + // Redis cache options. + REDIS_OPTIONS: { + host: '127.0.0.1', + port: 6379 + }, + */ + + /* + // Memcached options. See https://github.com/3rd-Eden/node-memcached#server-locations + MEMCACHED_OPTIONS: { + locations: "127.0.0.1:11211" + } + */ + + /* + // Access-Control-Allow-Origin list. + allowedOrigins: [ + "*", + "http://another_domain.com" + ], + */ + + /* + // Uncomment to enable plugin testing framework. + tests: { + mongodb: 'mongodb://localhost:27017/iframely-tests', + single_test_timeout: 10 * 1000, + plugin_test_period: 2 * 60 * 60 * 1000, + relaunch_script_period: 5 * 60 * 1000 + }, + */ + + // If there's no response from remote server, the timeout will occur after + RESPONSE_TIMEOUT: 5 * 1000, //ms + + /* From v1.4.0, Iframely supports HTTP/2 by default. Disable it, if you'd rather not. + Alternatively, you can also disable per origin. See `proxy` option below. + */ + // DISABLE_HTTP2: true, + + // Customize API calls to oembed endpoints. + ADD_OEMBED_PARAMS: [{ + // Endpoint url regexp array. + re: [/^http:\/\/api\.instagram\.com\/oembed/], + // Custom get params object. + params: { + hidecaption: true + } + }, { + re: [/^https:\/\/www\.facebook\.com\/plugins\/page\/oembed\.json/i], + params: { + show_posts: 0, + show_facepile: 0, + maxwidth: 600 + } + }, { + // match i=user or i=moment or i=timeline to configure these types invidually + // see params spec at https://dev.twitter.com/web/embedded-timelines/oembed + re: [/^https?:\/\/publish\.twitter\.com\/oembed\?i=user/i], + params: { + limit: 1, + maxwidth: 600 + } + /* + }, { + // Facebook https://developers.facebook.com/docs/plugins/oembed-endpoints + re: [/^https:\/\/www\.facebook\.com\/plugins\/\w+\/oembed\.json/i], + params: { + // Skip script tag and fb-root div. + omitscript: true + } + */ + }], + + /* + // Configure use of HTTP proxies as needed. + // You don't have to specify all options per regex - just what you need to override + PROXY: [{ + re: [/^https?:\/\/www\.domain\.com/], + proxy_server: 'http://1.2.3.4:8080', + user_agent: 'CHANGE YOUR AGENT', + headers: { + // HTTP headers + // Overrides previous params if overlapped. + }, + request_options: { + // Refer to: https://github.com/request/request + // Overrides previous params if overlapped. + }, + disable_http2: true + }], + */ + + // Customize API calls to 3rd parties. At the very least - configure required keys. + providerOptions: { + locale: "en_US", // ISO 639-1 two-letter language code, e.g. en_CA or fr_CH. + // Will be added as highest priotity in accept-language header with each request. + // Plus is used in FB, YouTube and perhaps other plugins + "twitter": { + "max-width": 550, + "min-width": 250, + hide_media: false, + hide_thread: false, + omit_script: false, + center: false, + // dnt: true, + cache_ttl: 100 * 365 * 24 * 3600 // 100 Years. + }, + readability: { + enabled: false + // allowPTagDescription: true // to enable description fallback to first paragraph + }, + images: { + loadSize: false, // if true, will try an load first bytes of all images to get/confirm the sizes + checkFavicon: false // if true, will verify all favicons + }, + tumblr: { + consumer_key: "INSERT YOUR VALUE" + // media_only: true // disables status embeds for images and videos - will return plain media + }, + google: { + // https://developers.google.com/maps/documentation/embed/guide#api_key + maps_key: "INSERT YOUR VALUE" + }, + + /* + // Optional Camo Proxy to wrap all images: https://github.com/atmos/camo + camoProxy: { + camo_proxy_key: "INSERT YOUR VALUE", + camo_proxy_host: "INSERT YOUR VALUE" + // ssl_only: true // will only proxy non-ssl images + }, + */ + + // List of query parameters to add to YouTube and Vimeo frames + // Start it with leading "?". Or omit alltogether for default values + // API key is optional, youtube will work without it too. + // It is probably the same API key you use for Google Maps. + youtube: { + // api_key: "INSERT YOUR VALUE", + get_params: "?rel=0&showinfo=1" // https://developers.google.com/youtube/player_parameters + }, + vimeo: { + get_params: "?byline=0&badge=0" // https://developer.vimeo.com/player/embedding + }, + + /* + soundcloud: { + old_player: true // enables classic player + }, + giphy: { + media_only: true // disables branded player for gifs and returns just the image + } + */ + /* + bandcamp: { + get_params: '/size=large/bgcol=333333/linkcol=ffffff/artwork=small/transparent=true/', + media: { + album: { + height: 472, + 'max-width': 700 + }, + track: { + height: 120, + 'max-width': 700 + } + } + } + */ + }, + + // WHITELIST_WILDCARD, if present, will be added to whitelist as record for top level domain: "*" + // with it, you can define what parsers do when they run accross unknown publisher. + // If absent or empty, all generic media parsers will be disabled except for known domains + // More about format: https://iframely.com/docs/qa-format + + /* + WHITELIST_WILDCARD: { + "twitter": { + "player": "allow", + "photo": "deny" + }, + "oembed": { + "video": "allow", + "photo": "allow", + "rich": "deny", + "link": "deny" + }, + "og": { + "video": ["allow", "ssl", "responsive"] + }, + "iframely": { + "survey": "allow", + "reader": "allow", + "player": "allow", + "image": "allow" + }, + "html-meta": { + "video": ["allow", "responsive"], + "promo": "allow" + } + } + */ + + // Black-list any of the inappropriate domains. Iframely will return 417 + // At minimum, keep your localhosts blacklisted to avoid SSRF + BLACKLIST_DOMAINS_RE: [ + /^https?:\/\/127\.0\.0\.1/i, + /^https?:\/\/localhost/i, + + // And this is AWS metadata service + // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html + /^https?:\/\/169\.254\.169\.254/ + ] + }; + + module.exports = config; +})(); diff --git a/docker/prod/docker-compose.yml b/docker/prod/docker-compose.yml index 3472be5d6..8469a1e7b 100644 --- a/docker/prod/docker-compose.yml +++ b/docker/prod/docker-compose.yml @@ -26,6 +26,14 @@ services: volumes: - lemmy_pictshare:/usr/share/nginx/html/data restart: always + lemmy_iframely: + image: dogbin/iframely:latest + ports: + - "127.0.0.1:8061:8061" + volumes: + - ./iframely.config.local.js:/iframely/config.local.js:ro + restart: always volumes: lemmy_db: lemmy_pictshare: + lemmy_iframely: diff --git a/docs/src/administration_install_docker.md b/docs/src/administration_install_docker.md index f92cbd5be..992049839 100644 --- a/docs/src/administration_install_docker.md +++ b/docs/src/administration_install_docker.md @@ -7,6 +7,7 @@ mkdir lemmy/ cd lemmy/ wget https://raw.githubusercontent.com/dessalines/lemmy/master/docker/prod/docker-compose.yml wget https://raw.githubusercontent.com/dessalines/lemmy/master/docker/lemmy.hjson +wget https://raw.githubusercontent.com/dessalines/lemmy/master/docker/iframely.config.local.js # Edit lemmy.hjson, and docker-compose.yml to do more configuration (like adding a custom password) docker-compose up -d ``` diff --git a/ui/src/components/iframely-card.tsx b/ui/src/components/iframely-card.tsx new file mode 100644 index 000000000..73f3cef72 --- /dev/null +++ b/ui/src/components/iframely-card.tsx @@ -0,0 +1,100 @@ +import { Component, linkEvent } from 'inferno'; +import { FramelyData } from '../interfaces'; +import { mdToHtml } from '../utils'; + +interface FramelyCardProps { + iframely: FramelyData; +} + +interface FramelyCardState { + expanded: boolean; +} + +export class IFramelyCard extends Component< + FramelyCardProps, + FramelyCardState +> { + private emptyState: FramelyCardState = { + expanded: false, + }; + + constructor(props: any, context: any) { + super(props, context); + this.state = this.emptyState; + } + + render() { + let iframely = this.props.iframely; + return ( + <> +
+
+ {iframely.thumbnail_url && ( +
+ {iframely.html ? ( + + + + ) : ( + + )} +
+ )} +
+
+
+ + + {iframely.title} + + +
+ + + {new URL(iframely.url).hostname} + + + + + {iframely.html && ( + + {this.state.expanded ? '[-]' : '[+]'} + + )} + + {iframely.description && ( +
+ )} +
+
+
+
+ {this.state.expanded && ( +
+
+
+ )} + + ); + } + + handleIframeExpand(i: IFramelyCard) { + i.state.expanded = !i.state.expanded; + i.setState(i.state); + } +} diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index d37725440..5cc632517 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -15,9 +15,11 @@ import { AddAdminForm, TransferSiteForm, TransferCommunityForm, + FramelyData, } from '../interfaces'; import { MomentTime } from './moment-time'; import { PostForm } from './post-form'; +import { IFramelyCard } from './iframely-card'; import { mdToHtml, canMod, @@ -47,6 +49,7 @@ interface PostListingState { score: number; upvotes: number; downvotes: number; + iframely: FramelyData; } interface PostListingProps { @@ -74,6 +77,7 @@ export class PostListing extends Component { score: this.props.post.score, upvotes: this.props.post.upvotes, downvotes: this.props.post.downvotes, + iframely: null, }; constructor(props: any, context: any) { @@ -84,6 +88,10 @@ export class PostListing extends Component { this.handlePostDisLike = this.handlePostDisLike.bind(this); this.handleEditPost = this.handleEditPost.bind(this); this.handleEditCancel = this.handleEditCancel.bind(this); + + if (this.props.post.url) { + this.fetchIframely(); + } } componentWillReceiveProps(nextProps: PostListingProps) { @@ -141,7 +149,7 @@ export class PostListing extends Component { )}
- {post.url && isImage(post.url) && !this.state.imageExpanded && ( + {this.hasImage() && !this.state.imageExpanded && ( { className={`mx-2 mt-1 float-left img-fluid thumbnail rounded ${(post.nsfw || post.community_nsfw) && 'img-blur'}`} - src={imageThumbnailer(post.url)} + src={imageThumbnailer(this.getImage())} /> )} @@ -205,7 +213,7 @@ export class PostListing extends Component {
)} - {post.url && isImage(post.url) && ( + {this.hasImage() && ( <> {!this.state.imageExpanded ? ( { class="pointer" onClick={linkEvent(this, this.handleImageExpandClick)} > - +
@@ -587,6 +598,9 @@ export class PostListing extends Component { )} + {post.url && this.props.showBody && this.state.iframely && ( + + )} {this.state.showRemoveDialog && (
{ ); } + fetchIframely() { + fetch(`/iframely/oembed?url=${this.props.post.url}`) + .then(res => res.json()) + .then(res => { + this.state.iframely = res; + this.setState(this.state); + }) + .catch(error => { + console.error(`Iframely service not set up properly. ${error}`); + }); + } + + hasImage(): boolean { + return ( + (this.props.post.url && isImage(this.props.post.url)) || + (this.state.iframely && this.state.iframely.thumbnail_url !== undefined) + ); + } + + getImage(): string { + let simpleImg = isImage(this.props.post.url); + if (simpleImg) { + return this.props.post.url; + } else if (this.state.iframely) { + let iframelyThumbnail = this.state.iframely.thumbnail_url; + if (iframelyThumbnail) { + return iframelyThumbnail; + } + } + } + handlePostLike(i: PostListing) { let new_vote = i.state.my_vote == 1 ? 0 : 1; diff --git a/ui/src/interfaces.ts b/ui/src/interfaces.ts index 5846b548c..5baadb170 100644 --- a/ui/src/interfaces.ts +++ b/ui/src/interfaces.ts @@ -876,3 +876,18 @@ export interface WebSocketJsonResponse { error?: string; reconnect?: boolean; } + +export interface FramelyData { + url: string; + type: string; + version?: string; + title: string; + author?: string; + author_url?: string; + provider_name?: string; + thumbnail_url?: string; + thumbnail_width?: number; + thumbnail_height?: number; + description?: string; + html?: string; +} From 6c28851fe88d2120ac84c118ff76ab28186a0788 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Mon, 17 Feb 2020 12:45:08 -0500 Subject: [PATCH 014/164] Only show it if it has a title. --- ui/src/components/iframely-card.tsx | 94 +++++++++++++++-------------- 1 file changed, 48 insertions(+), 46 deletions(-) diff --git a/ui/src/components/iframely-card.tsx b/ui/src/components/iframely-card.tsx index 73f3cef72..a9193528f 100644 --- a/ui/src/components/iframely-card.tsx +++ b/ui/src/components/iframely-card.tsx @@ -27,60 +27,62 @@ export class IFramelyCard extends Component< let iframely = this.props.iframely; return ( <> -
-
- {iframely.thumbnail_url && ( -
- {iframely.html ? ( - - - - ) : ( - - )} -
- )} -
-
-
- - - {iframely.title} - - -
- - - {new URL(iframely.url).hostname} - - - - - {iframely.html && ( + {iframely.title && ( +
+
+ {iframely.thumbnail_url && ( +
+ {iframely.html ? ( - {this.state.expanded ? '[-]' : '[+]'} + + ) : ( + )} - - {iframely.description && ( -
- )} +
+ )} +
+
+
+ + + {iframely.title} + + +
+ + + {new URL(iframely.url).hostname} + + + + + {iframely.html && ( + + {this.state.expanded ? '[-]' : '[+]'} + + )} + + {iframely.description && ( +
+ )} +
-
+ )} {this.state.expanded && (
Date: Mon, 17 Feb 2020 12:55:43 -0500 Subject: [PATCH 015/164] Remove the responsive bootstrap utils. --- ui/src/components/iframely-card.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/ui/src/components/iframely-card.tsx b/ui/src/components/iframely-card.tsx index a9193528f..ea237edb3 100644 --- a/ui/src/components/iframely-card.tsx +++ b/ui/src/components/iframely-card.tsx @@ -84,12 +84,10 @@ export class IFramelyCard extends Component<
)} {this.state.expanded && ( -
-
-
+
)} ); From 50600938cba4b8cd395a5d87b2e7e3471091dcd9 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Mon, 17 Feb 2020 13:04:53 -0500 Subject: [PATCH 016/164] Version v0.6.20 --- ansible/VERSION | 2 +- docker/prod/docker-compose.yml | 2 +- server/src/version.rs | 2 +- ui/src/version.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ansible/VERSION b/ansible/VERSION index 6139a0061..672c01a45 100644 --- a/ansible/VERSION +++ b/ansible/VERSION @@ -1 +1 @@ -v0.6.19 +v0.6.20 diff --git a/docker/prod/docker-compose.yml b/docker/prod/docker-compose.yml index 8469a1e7b..4ffe00867 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.19 + image: dessalines/lemmy:v0.6.20 ports: - "127.0.0.1:8536:8536" restart: always diff --git a/server/src/version.rs b/server/src/version.rs index cd7310c47..220cd9ba5 100644 --- a/server/src/version.rs +++ b/server/src/version.rs @@ -1 +1 @@ -pub const VERSION: &str = "v0.6.19"; +pub const VERSION: &str = "v0.6.20"; diff --git a/ui/src/version.ts b/ui/src/version.ts index c58b46d3f..90de0134d 100644 --- a/ui/src/version.ts +++ b/ui/src/version.ts @@ -1 +1 @@ -export const version: string = 'v0.6.19'; +export const version: string = 'v0.6.20'; From d26557fac3d8d39d5347f6be210cdbf9b321e4a4 Mon Sep 17 00:00:00 2001 From: Andre Vallestero Date: Mon, 17 Feb 2020 13:54:22 -0500 Subject: [PATCH 017/164] Completed french translations, verified syntax --- ui/src/translations/fr.ts | 46 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/ui/src/translations/fr.ts b/ui/src/translations/fr.ts index f16af7bfc..7e8a6ba9c 100644 --- a/ui/src/translations/fr.ts +++ b/ui/src/translations/fr.ts @@ -9,7 +9,8 @@ export const fr = { posts: 'Publications', related_posts: 'Ces sujets peuvent être corrélés', cross_posts: 'Ce sujet a également été posté sur :', - cross_post: 'crosspost', + cross_post: 'publication croisée', + cross_posted_to: 'publication croisée à', comments: 'Commentaires', number_of_comments: '{{count}} Commentaires', remove_comment: 'Supprimer le commentaire', @@ -23,11 +24,18 @@ export const fr = { list_of_communities: 'Liste des communautés', number_of_communities: '{{count}} communautés', community_reqs: 'en minuscule, sans espace et avec tiret du bas.', + create_private_message: 'Créer un message privé', + send_secure_message: 'Envoyer le message sécurisé', + send_message: 'Enovyer le message', + message: 'Message', edit: 'éditer', reply: 'répondre', cancel: 'Annuler', preview: 'prévisualiser', upload_image: 'envoyer une image', + avatar: 'Avatar', + upload_avatar: 'Télécharger une avatar', + show_avatar: 'Afficher les avatars', formatting_help: 'aide au formattage', view_source: 'voir la source', unlock: 'débloquer', @@ -35,6 +43,7 @@ export const fr = { sticky: 'épingler', unsticky: 'décrocher', link: 'lien', + archive_link: 'archiver le lien', mod: 'modérateur', mods: 'modérateurs', moderates: 'Modérer', @@ -89,6 +98,7 @@ export const fr = { sort_type: 'Trier', hot: 'Tendances', new: 'Nouveaux', + old: 'Ancien', top_day: 'Top du jour', week: 'Semaine', month: 'Mois', @@ -96,12 +106,16 @@ export const fr = { all: 'Tout', top: 'Top', api: 'API', + docs: 'Documentations', inbox: 'Boîte de réception', inbox_for: 'Boîte de réception de <1>{{user}}', mark_all_as_read: 'Tout marquer comme lu', type: 'Type', unread: 'Non-lu', + replies: 'Réponses', + mentions: 'Mentions', reply_sent: 'Réponse envoyée', + message_sent: 'Message envoyé', search: 'Rechercher', overview: 'Général', view: 'Voir', @@ -112,11 +126,29 @@ export const fr = { notifications_error: 'Les notifications de bureau ne sont pas discponibles sur votre navigateur. Essayez Firefox ou Chrome.', unread_messages: 'Messages non-lu', + messages: 'Messages', password: 'Mot de passe', verify_password: 'Vérifiez le mot de passe', + old_password: 'Ancien mot de passe', + forgot_password: 'Mot de passe oublié', + reset_password_mail_sent: 'Un email a été envoyé pour réinitialiser votre mot de passe.', + password_change: '----------------------------------------------', + new_password: 'Nouveau mot de passe', + no_email_setup: "Ce serveur n'a pas correctement configuré la messagerie de email.", email: 'Email', + matrix_user_id: 'Utilisateur Matrix', + private_message_disclaimer: + "Attention: les messages privés en Matrix ne sont pas sécurisés. S'il vous plait, créer un compte de <1>Riot.im pour des messages sécurisés.", + send_notifications_to_email: 'Envoyer des notifications par email', optional: 'Optionnel', expires: 'Expire', + language: 'Langue', + browser_default: 'Défaut pour le navigateur', + downvotes_disabled: 'Votes négatifs désactivés', + enable_downvotes: 'Votes négatifs activés', + open_registration: 'Ouvrir la regestration', + registration_closed: 'Régestration fermée', + enable_nsfw: 'Activer NSFW', url: 'URL', body: 'Texte', copy_suggested_title: 'Ajouter le titre suggéré: {{title}}', @@ -140,6 +172,8 @@ export const fr = { "Lemmy est gratuit et <1>open-source, c'est à dire sans publicité et sans monétisation. Pour toujours. Vos dons soutiennent directement le développement du projet. Merci à nos soutiens.", support_on_patreon: 'Soutenir sur Patreon', support_on_liberapay: 'Soutenir sur Liberapay', + donate_to_lemmy: 'Faire un don à reddit', + donate: 'Faire un don', general_sponsors: 'Les sponsors généraux sont ceux garantissant de 10 à 39$.', crypto: 'Cryptomonnaies', bitcoin: 'Bitcoin', @@ -149,6 +183,7 @@ export const fr = { joined: 'Membre depuis', by: 'par', to: 'vers', + from: 'de', transfer_community: 'transférer la communauté', transfer_site: 'transférer le site', are_you_sure: 'Êtes-vous sûr ?', @@ -158,12 +193,14 @@ export const fr = { landing_0: 'Lemmy est un <1>aggrégateur de lien, similaire à reddit et conçu pour fonctionner sur le <2>fédiverse.<3>Il est auto-hébergeable, se met à jour en direct et est léger (<4>~80kB). La fédération via Activitypub est prévue. <5>Lemmy est une <6>version beta très précoce, et de nombreuses fonctionnalités sont manquantes ou non fonctionnelles. <7>Vous pouvez rapporter des bugs et suggérez de nouvelles fonctionnalités <8>ici.<9>Crée avec <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.', not_logged_in: "Vous n'êtes pas connecté.", + logged_in: 'Vous êtes connecté.', community_ban: 'Vous avez été banni de cette communauté.', site_ban: 'Vous avez été banni du site', couldnt_create_comment: 'Impossible de poster le commentaire.', couldnt_like_comment: "Impossible d'aimer le commentaire.", couldnt_update_comment: 'Impossible de mettre à jour le commentaire.', couldnt_save_comment: 'Impossible de sauvegarder le commentaire.', + couldnt_get_comments: 'Impossible de obtenir les commentaires.', no_comment_edit_allowed: "Vous n'êtes pas autorisé à éditer ce commentaire.", no_post_edit_allowed: "Vous n'êtes pas autorisé à éditer sujet.", @@ -176,6 +213,7 @@ export const fr = { community_follower_already_exists: 'Ce membre est déjà abonné.', community_user_already_banned: 'Ce membre est déjà banni.', couldnt_create_post: 'Impossible de créer le sujet.', + post_title_too_long: 'Sujet titre trop long.', couldnt_like_post: "Impossible d'aimer le sujet.", couldnt_find_post: 'Impossible de trouver le sujet.', couldnt_get_posts: "Impossible d'obtenir les sujets", @@ -191,8 +229,14 @@ export const fr = { passwords_dont_match: 'Les mots de passes ne correspondent pas..', admin_already_created: 'Désolé, il y a déjà un admin.', user_already_exists: "L'utilisateur existe déjà.", + email_already_exists: "l'email existe déjà", couldnt_update_user: "Impossible de mettre à jour l'utilisateur.", system_err_login: 'Erreur système. Essayez de vous déconneter puis de vous reconnecter.', + couldnt_create_private_message: 'Impossible de créer un message privé.', + no_private_message_edit_allowed: 'Pas autorisé à modifier un message privé.', + couldnt_update_private_message: 'Impossible de modifier un message privé.', + time: 'Temps', + action: 'Action', }, }; From 702b55d16b09cbb194f99644d9ee835b3c39fd60 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Mon, 17 Feb 2020 14:06:19 -0500 Subject: [PATCH 018/164] Add oembed to readme. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 47290953c..be92c428f 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,7 @@ Each lemmy server can set its own moderation policy; appointing site-wide admins - Can transfer site and communities to others. - Can fully erase your data, replacing all posts and comments. - NSFW post / community support. +- OEmbed support via Iframely. - High performance. - Server is written in rust. - Front end is `~80kB` gzipped. @@ -136,7 +137,7 @@ fa | 71% | cross_post,cross_posted_to,subscribed_to_communities,trending_communi eo | 73% | cross_posted_to,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,support_on_liberapay,donate_to_lemmy,donate,from,are_you_sure,yes,no,logged_in,couldnt_get_comments,post_title_too_long,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message,time,action es | 99% | cross_posted_to,couldnt_get_comments,post_title_too_long fi | 97% | cross_posted_to,old,support_on_liberapay,couldnt_get_comments,post_title_too_long,time,action -fr | 81% | cross_posted_to,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,couldnt_get_comments,post_title_too_long,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message,time,action +fr | 100% | show_avatars it | 82% | cross_posted_to,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,couldnt_get_comments,post_title_too_long,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message,time,action nl | 98% | cross_posted_to,couldnt_get_comments,post_title_too_long,time,action pt-br | 99% | couldnt_get_comments,post_title_too_long From 2fbcb2b23f65e3da0483f25eb208ef0d2b3b6a20 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Mon, 17 Feb 2020 14:37:36 -0500 Subject: [PATCH 019/164] Removing images from iframely cards. --- ui/src/components/iframely-card.tsx | 31 +++++++++-------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/ui/src/components/iframely-card.tsx b/ui/src/components/iframely-card.tsx index ea237edb3..b0a0d34b3 100644 --- a/ui/src/components/iframely-card.tsx +++ b/ui/src/components/iframely-card.tsx @@ -27,27 +27,10 @@ export class IFramelyCard extends Component< let iframely = this.props.iframely; return ( <> - {iframely.title && ( -
-
- {iframely.thumbnail_url && ( -
- {iframely.html ? ( - - - - ) : ( - - )} -
- )} -
+ {iframely.title && !this.state.expanded && ( +
+
+
@@ -57,7 +40,11 @@ export class IFramelyCard extends Component<
- + {new URL(iframely.url).hostname} From 1e5d93d9bba1051e8182dea20974359f9539f3f9 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Mon, 17 Feb 2020 14:38:16 -0500 Subject: [PATCH 020/164] Version v0.6.21 --- ansible/VERSION | 2 +- docker/prod/docker-compose.yml | 2 +- server/src/version.rs | 2 +- ui/src/version.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ansible/VERSION b/ansible/VERSION index 672c01a45..a0387771c 100644 --- a/ansible/VERSION +++ b/ansible/VERSION @@ -1 +1 @@ -v0.6.20 +v0.6.21 diff --git a/docker/prod/docker-compose.yml b/docker/prod/docker-compose.yml index 4ffe00867..e1cf6b250 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.20 + image: dessalines/lemmy:v0.6.21 ports: - "127.0.0.1:8536:8536" restart: always diff --git a/server/src/version.rs b/server/src/version.rs index 220cd9ba5..798dce2e7 100644 --- a/server/src/version.rs +++ b/server/src/version.rs @@ -1 +1 @@ -pub const VERSION: &str = "v0.6.20"; +pub const VERSION: &str = "v0.6.21"; diff --git a/ui/src/version.ts b/ui/src/version.ts index 90de0134d..853731e0d 100644 --- a/ui/src/version.ts +++ b/ui/src/version.ts @@ -1 +1 @@ -export const version: string = 'v0.6.20'; +export const version: string = 'v0.6.21'; From 05648173aecdd883c1ef6c061cf2f32dad034b85 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Mon, 17 Feb 2020 16:00:18 -0500 Subject: [PATCH 021/164] Fix dynamic post url changing issue. --- ui/src/components/iframely-card.tsx | 4 +-- ui/src/components/post-listing.tsx | 43 +++++++++++++++++------------ 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/ui/src/components/iframely-card.tsx b/ui/src/components/iframely-card.tsx index b0a0d34b3..4bae06d1c 100644 --- a/ui/src/components/iframely-card.tsx +++ b/ui/src/components/iframely-card.tsx @@ -52,7 +52,7 @@ export class IFramelyCard extends Component< {iframely.html && ( {this.state.expanded ? '[-]' : '[+]'} @@ -72,7 +72,7 @@ export class IFramelyCard extends Component< )} {this.state.expanded && (
)} diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index 5cc632517..7b3d64798 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -49,6 +49,7 @@ interface PostListingState { score: number; upvotes: number; downvotes: number; + url: string; iframely: FramelyData; } @@ -77,6 +78,7 @@ export class PostListing extends Component { score: this.props.post.score, upvotes: this.props.post.upvotes, downvotes: this.props.post.downvotes, + url: this.props.post.url, iframely: null, }; @@ -89,7 +91,7 @@ export class PostListing extends Component { this.handleEditPost = this.handleEditPost.bind(this); this.handleEditCancel = this.handleEditCancel.bind(this); - if (this.props.post.url) { + if (this.state.url) { this.fetchIframely(); } } @@ -99,6 +101,13 @@ export class PostListing extends Component { this.state.upvotes = nextProps.post.upvotes; this.state.downvotes = nextProps.post.downvotes; this.state.score = nextProps.post.score; + this.state.url = nextProps.post.url; + this.state.iframely = null; + + if (nextProps.post.url) { + this.fetchIframely(); + } + this.setState(this.state); } @@ -163,7 +172,7 @@ export class PostListing extends Component { /> )} - {post.url && isVideo(post.url) && ( + {this.state.url && isVideo(this.state.url) && ( )}
- {this.props.showBody && post.url ? ( + {this.props.showBody && this.state.url ? ( {post.name} @@ -198,15 +207,15 @@ export class PostListing extends Component { )}
- {post.url && ( + {this.state.url && ( - {new URL(post.url).hostname} + {new URL(this.state.url).hostname} @@ -598,7 +607,7 @@ export class PostListing extends Component { )} - {post.url && this.props.showBody && this.state.iframely && ( + {this.state.url && this.props.showBody && this.state.iframely && ( )} {this.state.showRemoveDialog && ( @@ -752,7 +761,7 @@ export class PostListing extends Component { } fetchIframely() { - fetch(`/iframely/oembed?url=${this.props.post.url}`) + fetch(`/iframely/oembed?url=${this.state.url}`) .then(res => res.json()) .then(res => { this.state.iframely = res; @@ -765,15 +774,15 @@ export class PostListing extends Component { hasImage(): boolean { return ( - (this.props.post.url && isImage(this.props.post.url)) || + (this.state.url && isImage(this.state.url)) || (this.state.iframely && this.state.iframely.thumbnail_url !== undefined) ); } getImage(): string { - let simpleImg = isImage(this.props.post.url); + let simpleImg = isImage(this.state.url); if (simpleImg) { - return this.props.post.url; + return this.state.url; } else if (this.state.iframely) { let iframelyThumbnail = this.state.iframely.thumbnail_url; if (iframelyThumbnail) { @@ -877,8 +886,8 @@ export class PostListing extends Component { get crossPostParams(): string { let params = `?title=${this.props.post.name}`; - if (this.props.post.url) { - params += `&url=${this.props.post.url}`; + if (this.state.url) { + params += `&url=${this.state.url}`; } if (this.props.post.body) { params += `&body=${this.props.post.body}`; From fd82487ab52f76a81087a5a4af109b95a8a82d42 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Mon, 17 Feb 2020 16:11:49 -0500 Subject: [PATCH 022/164] Version v0.6.22 --- ansible/VERSION | 2 +- docker/prod/docker-compose.yml | 2 +- server/src/version.rs | 2 +- ui/src/version.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ansible/VERSION b/ansible/VERSION index a0387771c..635026fb8 100644 --- a/ansible/VERSION +++ b/ansible/VERSION @@ -1 +1 @@ -v0.6.21 +v0.6.22 diff --git a/docker/prod/docker-compose.yml b/docker/prod/docker-compose.yml index e1cf6b250..2ada25123 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.21 + image: dessalines/lemmy:v0.6.22 ports: - "127.0.0.1:8536:8536" restart: always diff --git a/server/src/version.rs b/server/src/version.rs index 798dce2e7..e4fc27d2a 100644 --- a/server/src/version.rs +++ b/server/src/version.rs @@ -1 +1 @@ -pub const VERSION: &str = "v0.6.21"; +pub const VERSION: &str = "v0.6.22"; diff --git a/ui/src/version.ts b/ui/src/version.ts index 853731e0d..0e9211a2b 100644 --- a/ui/src/version.ts +++ b/ui/src/version.ts @@ -1 +1 @@ -export const version: string = 'v0.6.21'; +export const version: string = 'v0.6.22'; From 9ab038d273813daad122ae01ebb03c3d64b2f787 Mon Sep 17 00:00:00 2001 From: Andre Vallestero Date: Mon, 17 Feb 2020 17:20:03 -0500 Subject: [PATCH 023/164] Corrected show_avatar to show_avatars --- ui/src/translations/fr.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/translations/fr.ts b/ui/src/translations/fr.ts index 7e8a6ba9c..de3d158ba 100644 --- a/ui/src/translations/fr.ts +++ b/ui/src/translations/fr.ts @@ -34,7 +34,7 @@ export const fr = { preview: 'prévisualiser', upload_image: 'envoyer une image', avatar: 'Avatar', - upload_avatar: 'Télécharger une avatar', + upload_avatars: 'Télécharger une avatar', show_avatar: 'Afficher les avatars', formatting_help: 'aide au formattage', view_source: 'voir la source', From dd28e6fbeb623c2f7fd5d38692fafae24dd2d14e Mon Sep 17 00:00:00 2001 From: Andre Vallestero Date: Mon, 17 Feb 2020 17:30:19 -0500 Subject: [PATCH 024/164] Corrections made, added text for password change --- ui/src/translations/fr.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/src/translations/fr.ts b/ui/src/translations/fr.ts index de3d158ba..abc3332dd 100644 --- a/ui/src/translations/fr.ts +++ b/ui/src/translations/fr.ts @@ -132,7 +132,7 @@ export const fr = { old_password: 'Ancien mot de passe', forgot_password: 'Mot de passe oublié', reset_password_mail_sent: 'Un email a été envoyé pour réinitialiser votre mot de passe.', - password_change: '----------------------------------------------', + password_change: 'Changement de mot de passe', new_password: 'Nouveau mot de passe', no_email_setup: "Ce serveur n'a pas correctement configuré la messagerie de email.", email: 'Email', @@ -229,7 +229,7 @@ export const fr = { passwords_dont_match: 'Les mots de passes ne correspondent pas..', admin_already_created: 'Désolé, il y a déjà un admin.', user_already_exists: "L'utilisateur existe déjà.", - email_already_exists: "l'email existe déjà", + email_already_exists: "L'email existe déjà", couldnt_update_user: "Impossible de mettre à jour l'utilisateur.", system_err_login: 'Erreur système. Essayez de vous déconneter puis de vous reconnecter.', From 022d632e40795b8760209e0b1d40b62c8ffd8040 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Mon, 17 Feb 2020 18:26:39 -0500 Subject: [PATCH 025/164] Updating translation report. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index be92c428f..b55d678fc 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,7 @@ fa | 71% | cross_post,cross_posted_to,subscribed_to_communities,trending_communi eo | 73% | cross_posted_to,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,support_on_liberapay,donate_to_lemmy,donate,from,are_you_sure,yes,no,logged_in,couldnt_get_comments,post_title_too_long,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message,time,action es | 99% | cross_posted_to,couldnt_get_comments,post_title_too_long fi | 97% | cross_posted_to,old,support_on_liberapay,couldnt_get_comments,post_title_too_long,time,action -fr | 100% | show_avatars +fr | 100% | upload_avatar,show_avatars it | 82% | cross_posted_to,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,couldnt_get_comments,post_title_too_long,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message,time,action nl | 98% | cross_posted_to,couldnt_get_comments,post_title_too_long,time,action pt-br | 99% | couldnt_get_comments,post_title_too_long From 29822c13af6eb531b21b4648805eb6ea78217e23 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Mon, 17 Feb 2020 20:48:17 -0500 Subject: [PATCH 026/164] Add a user guide. Fixes #543 --- README.md | 1 + docs/src/SUMMARY.md | 1 + docs/src/about_guide.md | 28 ++++++++++++++++++++++++++++ ui/src/utils.ts | 2 +- 4 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 docs/src/about_guide.md diff --git a/README.md b/README.md index b55d678fc..a18b210b2 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,7 @@ Each lemmy server can set its own moderation policy; appointing site-wide admins - [Docker](https://dev.lemmy.ml/docs/administration_install_docker.html) - [Ansible](https://dev.lemmy.ml/docs/administration_install_ansible.html) - [Kubernetes](https://dev.lemmy.ml/docs/administration_install_kubernetes.html) + ## Support / Donate Lemmy is free, open-source software, meaning no advertising, monetizing, or venture capital, ever. Your donations directly support full-time development of the project. diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index c2df6223e..0514cbcda 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -4,6 +4,7 @@ - [Features](about_features.md) - [Goals](about_goals.md) - [Post and Comment Ranking](about_ranking.md) + - [Guide](about_guide.md) - [Administration](administration.md) - [Install with Docker](administration_install_docker.md) - [Install with Ansible](administration_install_ansible.md) diff --git a/docs/src/about_guide.md b/docs/src/about_guide.md new file mode 100644 index 000000000..426d659a0 --- /dev/null +++ b/docs/src/about_guide.md @@ -0,0 +1,28 @@ +# Lemmy Guide + +Start typing... + +- `@a_user_name` to get a list of usernames. +- `#a_community` to get a list of communities. +- `:emoji` to get a list of emojis. + +## Markdown Guide + +Type | Or | … to Get +--- | --- | --- +\*Italic\* | \_Italic\_ | _Italic_ +\*\*Bold\*\* | \_\_Bold\_\_ | **Bold** +\# Heading 1 | Heading 1
========= | # Heading 1 +\## Heading 2 | Heading 2
--------- | ## Heading 2 +\[Link\](http://a.com) | \[Link\]\[1\]

\[1\]: http://b.org | [Link](https://commonmark.org/) +!\[Image\](http://url/a.png) | !\[Image\]\[1\]

\[1\]: http://url/b.jpg | ![Markdown](https://commonmark.org/help/images/favicon.png) +\> Blockquote | | > Blockquote +\* List
\* List
\* List | \- List
\- List
\- List
| * List
* List
* List
+1\. One
2\. Two
3\. Three | 1) One
2) Two
3) Three | 1. One
2. Two
3. Three +Horizontal Rule
\--- | Horizontal Rule
\*\*\* | Horizontal Rule
* * * +\`Inline code\` with backticks | |`Inline code` with backticks +\`\`\`
\# code block
print '3 backticks or'
print 'indent 4 spaces'
\`\`\` | ····\# code block
····print '3 backticks or'
····print 'indent 4 spaces' | \# code block
print '3 backticks or'
print 'indent 4 spaces' +::: spoiler hidden stuff
*a bunch of spoilers here*
::: | |
hidden stuff

a bunch of spoilers here

+ +[CommonMark Tutorial](https://commonmark.org/help/tutorial/) + diff --git a/ui/src/utils.ts b/ui/src/utils.ts index 329168d26..1d5615abb 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -42,7 +42,7 @@ import emojiShortName from 'emoji-short-name'; import Toastify from 'toastify-js'; export const repoUrl = 'https://github.com/dessalines/lemmy'; -export const markdownHelpUrl = 'https://commonmark.org/help/'; +export const markdownHelpUrl = '/docs/about_guide.html'; export const archiveUrl = 'https://archive.is'; export const postRefetchSeconds: number = 60 * 1000; From 2062d07eb7834313bebaf27e69c8506c206fc68c Mon Sep 17 00:00:00 2001 From: Dessalines Date: Mon, 17 Feb 2020 20:54:45 -0500 Subject: [PATCH 027/164] Fixing about guide. --- docs/src/about_guide.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/src/about_guide.md b/docs/src/about_guide.md index 426d659a0..f22e201be 100644 --- a/docs/src/about_guide.md +++ b/docs/src/about_guide.md @@ -12,17 +12,17 @@ Type | Or | … to Get --- | --- | --- \*Italic\* | \_Italic\_ | _Italic_ \*\*Bold\*\* | \_\_Bold\_\_ | **Bold** -\# Heading 1 | Heading 1
========= | # Heading 1 -\## Heading 2 | Heading 2
--------- | ## Heading 2 +\# Heading 1 | Heading 1
========= |

Heading 1

+\## Heading 2 | Heading 2
--------- |
Heading 2
\[Link\](http://a.com) | \[Link\]\[1\]

\[1\]: http://b.org | [Link](https://commonmark.org/) !\[Image\](http://url/a.png) | !\[Image\]\[1\]

\[1\]: http://url/b.jpg | ![Markdown](https://commonmark.org/help/images/favicon.png) -\> Blockquote | | > Blockquote +\> Blockquote | |
Blockquote
\* List
\* List
\* List | \- List
\- List
\- List
| * List
* List
* List
1\. One
2\. Two
3\. Three | 1) One
2) Two
3) Three | 1. One
2. Two
3. Three -Horizontal Rule
\--- | Horizontal Rule
\*\*\* | Horizontal Rule
* * * +Horizontal Rule
\--- | Horizontal Rule
\*\*\* | Horizontal Rule

\`Inline code\` with backticks | |`Inline code` with backticks \`\`\`
\# code block
print '3 backticks or'
print 'indent 4 spaces'
\`\`\` | ····\# code block
····print '3 backticks or'
····print 'indent 4 spaces' | \# code block
print '3 backticks or'
print 'indent 4 spaces' -::: spoiler hidden stuff
*a bunch of spoilers here*
::: | |
hidden stuff

a bunch of spoilers here

+::: spoiler hidden or nsfw stuff
*a bunch of spoilers here*
::: | |
hidden or nsfw stuff

a bunch of spoilers here

[CommonMark Tutorial](https://commonmark.org/help/tutorial/) From 59ab341a2805bfe029d8e73fef4eaa12f5de9f30 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Tue, 18 Feb 2020 09:00:17 -0500 Subject: [PATCH 028/164] Fix iframely always refetching bug. --- ui/src/components/post-listing.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index 7b3d64798..50e1f30c9 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -101,11 +101,14 @@ export class PostListing extends Component { this.state.upvotes = nextProps.post.upvotes; this.state.downvotes = nextProps.post.downvotes; this.state.score = nextProps.post.score; - this.state.url = nextProps.post.url; - this.state.iframely = null; - if (nextProps.post.url) { - this.fetchIframely(); + if (nextProps.post.url !== this.state.url) { + this.state.url = nextProps.post.url; + if (this.state.url) { + this.fetchIframely(); + } else { + this.state.iframely = null; + } } this.setState(this.state); From 7d577f137f9083112a3be672d3bc82f44f2a89f5 Mon Sep 17 00:00:00 2001 From: Andre Vallestero Date: Tue, 18 Feb 2020 10:18:08 -0500 Subject: [PATCH 029/164] Fixed incorrect keys --- ui/src/translations/fr.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/src/translations/fr.ts b/ui/src/translations/fr.ts index abc3332dd..f54f48a25 100644 --- a/ui/src/translations/fr.ts +++ b/ui/src/translations/fr.ts @@ -34,8 +34,8 @@ export const fr = { preview: 'prévisualiser', upload_image: 'envoyer une image', avatar: 'Avatar', - upload_avatars: 'Télécharger une avatar', - show_avatar: 'Afficher les avatars', + upload_avatar: 'Télécharger une avatar', + show_avatars: 'Afficher les avatars', formatting_help: 'aide au formattage', view_source: 'voir la source', unlock: 'débloquer', From b18c87b9c424ceae65f6e946ad6726f74b6f907c Mon Sep 17 00:00:00 2001 From: Dessalines Date: Tue, 18 Feb 2020 12:37:35 -0500 Subject: [PATCH 030/164] Version v0.6.23 --- ansible/VERSION | 2 +- docker/prod/docker-compose.yml | 2 +- server/src/version.rs | 2 +- ui/src/version.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ansible/VERSION b/ansible/VERSION index 635026fb8..d44996fff 100644 --- a/ansible/VERSION +++ b/ansible/VERSION @@ -1 +1 @@ -v0.6.22 +v0.6.23 diff --git a/docker/prod/docker-compose.yml b/docker/prod/docker-compose.yml index 2ada25123..382ecce1c 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.22 + image: dessalines/lemmy:v0.6.23 ports: - "127.0.0.1:8536:8536" restart: always diff --git a/server/src/version.rs b/server/src/version.rs index e4fc27d2a..0a2225c16 100644 --- a/server/src/version.rs +++ b/server/src/version.rs @@ -1 +1 @@ -pub const VERSION: &str = "v0.6.22"; +pub const VERSION: &str = "v0.6.23"; diff --git a/ui/src/version.ts b/ui/src/version.ts index 0e9211a2b..7d9c8910c 100644 --- a/ui/src/version.ts +++ b/ui/src/version.ts @@ -1 +1 @@ -export const version: string = 'v0.6.22'; +export const version: string = 'v0.6.23'; From f36f44df7481f4d053b6dc84d512c6a8cd57b3dd Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 19 Feb 2020 13:35:15 -0500 Subject: [PATCH 031/164] Don't show post url if its local. --- README.md | 2 +- ui/src/components/post-listing.tsx | 33 ++++++++++++++++-------------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index a18b210b2..81eceb74a 100644 --- a/README.md +++ b/README.md @@ -138,7 +138,7 @@ fa | 71% | cross_post,cross_posted_to,subscribed_to_communities,trending_communi eo | 73% | cross_posted_to,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,support_on_liberapay,donate_to_lemmy,donate,from,are_you_sure,yes,no,logged_in,couldnt_get_comments,post_title_too_long,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message,time,action es | 99% | cross_posted_to,couldnt_get_comments,post_title_too_long fi | 97% | cross_posted_to,old,support_on_liberapay,couldnt_get_comments,post_title_too_long,time,action -fr | 100% | upload_avatar,show_avatars +fr | 100% | it | 82% | cross_posted_to,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,couldnt_get_comments,post_title_too_long,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message,time,action nl | 98% | cross_posted_to,couldnt_get_comments,post_title_too_long,time,action pt-br | 99% | couldnt_get_comments,post_title_too_long diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index 50e1f30c9..c0ddedc18 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -210,21 +210,24 @@ export class PostListing extends Component { )} - {this.state.url && ( - -
- {new URL(this.state.url).hostname} - - - - - - )} + {this.state.url && + !( + new URL(this.state.url).hostname == window.location.hostname + ) && ( + + + {new URL(this.state.url).hostname} + + + + + + )} {this.hasImage() && ( <> {!this.state.imageExpanded ? ( From 883c8decde1dab35c4ab4c3e4719ee4bd773f4a1 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 19 Feb 2020 13:35:58 -0500 Subject: [PATCH 032/164] Version v0.6.24 --- ansible/VERSION | 2 +- docker/prod/docker-compose.yml | 2 +- server/src/version.rs | 2 +- ui/src/version.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ansible/VERSION b/ansible/VERSION index d44996fff..7a28637ab 100644 --- a/ansible/VERSION +++ b/ansible/VERSION @@ -1 +1 @@ -v0.6.23 +v0.6.24 diff --git a/docker/prod/docker-compose.yml b/docker/prod/docker-compose.yml index 382ecce1c..20267236c 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.23 + image: dessalines/lemmy:v0.6.24 ports: - "127.0.0.1:8536:8536" restart: always diff --git a/server/src/version.rs b/server/src/version.rs index 0a2225c16..67f9b75dc 100644 --- a/server/src/version.rs +++ b/server/src/version.rs @@ -1 +1 @@ -pub const VERSION: &str = "v0.6.23"; +pub const VERSION: &str = "v0.6.24"; diff --git a/ui/src/version.ts b/ui/src/version.ts index 7d9c8910c..545698d89 100644 --- a/ui/src/version.ts +++ b/ui/src/version.ts @@ -1 +1 @@ -export const version: string = 'v0.6.23'; +export const version: string = 'v0.6.24'; From 2dec9b5b10098201b6197441d1984f8ff3e4e909 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Fri, 21 Feb 2020 11:26:42 -0500 Subject: [PATCH 033/164] Adding a link overlay. Fixes #549 --- ui/assets/css/main.css | 10 +++++++++ ui/src/components/post-listing.tsx | 36 ++++++++++++++++++++---------- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/ui/assets/css/main.css b/ui/assets/css/main.css index e7784877b..000201773 100644 --- a/ui/assets/css/main.css +++ b/ui/assets/css/main.css @@ -187,3 +187,13 @@ hr { color: var(--white); border: unset; } + +.link-overlay { + position: absolute; + top: 0; + right: 0; + padding: 2px; + background: rgba(0,0,0,.4); + border-bottom-left-radius: 0.25rem !important; + border-top-right-radius: 0.25rem !important; +} diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index c0ddedc18..b3bde27fe 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -162,18 +162,30 @@ export class PostListing extends Component { )}
{this.hasImage() && !this.state.imageExpanded && ( - - - +
+ + + + + + + + +
)} {this.state.url && isVideo(this.state.url) && (
{this.hasImage() && !this.state.imageExpanded && (
- - - - + + + + + + + + + + +
)} {this.state.url && isVideo(this.state.url) && ( @@ -263,10 +270,14 @@ export class PostListing extends Component { class="pointer" onClick={linkEvent(this, this.handleImageExpandClick)} > - + data={this.getImage()} + > + + + +
diff --git a/ui/src/components/symbols.tsx b/ui/src/components/symbols.tsx index 0870efd71..559a97072 100644 --- a/ui/src/components/symbols.tsx +++ b/ui/src/components/symbols.tsx @@ -15,6 +15,12 @@ export class Symbols extends Component { xmlnsXlink="http://www.w3.org/1999/xlink" > + + image + + + + external-link From fb32acb1ed8ba647bb52bfdc213fdf376bbaac72 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Thu, 27 Feb 2020 12:55:23 -0500 Subject: [PATCH 036/164] Use image thumbnails from pictshare. Fixes #555 --- ui/assets/css/main.css | 10 --- ui/package.json | 4 +- ui/src/components/post-listing.tsx | 108 +++++++++++++++++------------ ui/src/utils.ts | 10 +-- 4 files changed, 72 insertions(+), 60 deletions(-) diff --git a/ui/assets/css/main.css b/ui/assets/css/main.css index 704cddae5..048f687ec 100644 --- a/ui/assets/css/main.css +++ b/ui/assets/css/main.css @@ -188,16 +188,6 @@ hr { border: unset; } -.img-expand-overlay { - position: absolute; - top: 0; - right: 0; - padding: 2px; - background: rgba(0,0,0,.4); - border-bottom-left-radius: 0.25rem !important; - border-top-right-radius: 0.25rem !important; -} - .link-overlay:hover { transition: .1s; opacity: 1; diff --git a/ui/package.json b/ui/package.json index 6d7ad7758..79756bc69 100644 --- a/ui/package.json +++ b/ui/package.json @@ -1,9 +1,9 @@ { "name": "lemmy", - "description": "A simple UI for lemmy", + "description": "The official Lemmy UI", "version": "1.0.0", "author": "Dessalines", - "license": "GPL-2.0-or-later", + "license": "AGPL-3.0-or-later", "main": "index.js", "scripts": { "build": "node fuse prod", diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index d52df9d1c..56c1f0d9c 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -51,6 +51,7 @@ interface PostListingState { downvotes: number; url: string; iframely: FramelyData; + thumbnail: string; } interface PostListingProps { @@ -80,6 +81,7 @@ export class PostListing extends Component { downvotes: this.props.post.downvotes, url: this.props.post.url, iframely: null, + thumbnail: null, }; constructor(props: any, context: any) { @@ -92,6 +94,7 @@ export class PostListing extends Component { this.handleEditCancel = this.handleEditCancel.bind(this); if (this.state.url) { + this.setThumbnail(); this.fetchIframely(); } } @@ -105,9 +108,11 @@ export class PostListing extends Component { if (nextProps.post.url !== this.state.url) { this.state.url = nextProps.post.url; if (this.state.url) { + this.setThumbnail(); this.fetchIframely(); } else { this.state.iframely = null; + this.state.thumbnail = null; } } @@ -132,6 +137,18 @@ export class PostListing extends Component { ); } + imgThumbnail() { + let post = this.props.post; + return ( + + ); + } + listing() { let post = this.props.post; return ( @@ -161,37 +178,32 @@ export class PostListing extends Component { )}
- {this.hasImage() && !this.state.imageExpanded && ( + {this.state.thumbnail && !this.state.imageExpanded && ( )} {this.state.url && isVideo(this.state.url) && ( @@ -247,7 +259,7 @@ export class PostListing extends Component { )} - {this.hasImage() && ( + {this.state.thumbnail && ( <> {!this.state.imageExpanded ? ( { > @@ -795,29 +807,39 @@ export class PostListing extends Component { .then(res => { this.state.iframely = res; this.setState(this.state); + + // Store and fetch the image in pictshare + if ( + this.state.iframely.thumbnail_url && + isImage(this.state.iframely.thumbnail_url) + ) { + fetch( + `/pictshare/api/geturl.php?url=${this.state.iframely.thumbnail_url}` + ) + .then(res => res.json()) + .then(res => { + let url = `${window.location.origin}/pictshare/${res.url}`; + if (res.filetype == 'mp4') { + url += '/raw'; + } + this.state.thumbnail = url; + this.setState(this.state); + }); + } }) .catch(error => { console.error(`Iframely service not set up properly. ${error}`); }); } - hasImage(): boolean { - return ( - (this.state.url && isImage(this.state.url)) || - (this.state.iframely && this.state.iframely.thumbnail_url !== undefined) - ); - } - - getImage(): string { + setThumbnail() { let simpleImg = isImage(this.state.url); if (simpleImg) { - return this.state.url; - } else if (this.state.iframely) { - let iframelyThumbnail = this.state.iframely.thumbnail_url; - if (iframelyThumbnail) { - return iframelyThumbnail; - } + this.state.thumbnail = this.state.url; + } else { + this.state.thumbnail = null; } + this.setState(this.state); } handlePostLike(i: PostListing) { diff --git a/ui/src/utils.ts b/ui/src/utils.ts index 1d5615abb..d329dcd58 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -220,9 +220,9 @@ export function routeSearchTypeToEnum(type: string): SearchType { } export async function getPageTitle(url: string) { - let res = await fetch(`https://textance.herokuapp.com/title/${url}`); - let data = await res.text(); - return data; + let res = await fetch(`/iframely/oembed?url=${url}`).then(res => res.json()); + let title = await res.title; + return title; } export function debounce( @@ -386,7 +386,7 @@ export function objectFlip(obj: any) { export function pictshareAvatarThumbnail(src: string): string { // sample url: http://localhost:8535/pictshare/gs7xuu.jpg let split = src.split('pictshare'); - let out = `${split[0]}pictshare/96x96${split[1]}`; + let out = `${split[0]}pictshare/96${split[1]}`; return out; } @@ -401,7 +401,7 @@ export function showAvatars(): boolean { export function imageThumbnailer(url: string): string { let split = url.split('pictshare'); if (split.length > 1) { - let out = `${split[0]}pictshare/192x192${split[1]}`; + let out = `${split[0]}pictshare/192${split[1]}`; return out; } else { return url; From 72c5bdbf9e788243df4b2ec1249495e4d7f4092c Mon Sep 17 00:00:00 2001 From: Dessalines Date: Thu, 27 Feb 2020 13:16:30 -0500 Subject: [PATCH 037/164] Version v0.6.25 --- ansible/VERSION | 2 +- docker/prod/docker-compose.yml | 2 +- server/src/version.rs | 2 +- ui/src/version.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ansible/VERSION b/ansible/VERSION index 7a28637ab..6095ec4eb 100644 --- a/ansible/VERSION +++ b/ansible/VERSION @@ -1 +1 @@ -v0.6.24 +v0.6.25 diff --git a/docker/prod/docker-compose.yml b/docker/prod/docker-compose.yml index 20267236c..ac46ece30 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.24 + image: dessalines/lemmy:v0.6.25 ports: - "127.0.0.1:8536:8536" restart: always diff --git a/server/src/version.rs b/server/src/version.rs index 67f9b75dc..e504bd466 100644 --- a/server/src/version.rs +++ b/server/src/version.rs @@ -1 +1 @@ -pub const VERSION: &str = "v0.6.24"; +pub const VERSION: &str = "v0.6.25"; diff --git a/ui/src/version.ts b/ui/src/version.ts index 545698d89..78b06fe35 100644 --- a/ui/src/version.ts +++ b/ui/src/version.ts @@ -1 +1 @@ -export const version: string = 'v0.6.24'; +export const version: string = 'v0.6.25'; From 862321aa3972d12d3d2ca2527c6aa573d078e99f Mon Sep 17 00:00:00 2001 From: Dessalines Date: Thu, 27 Feb 2020 14:06:29 -0500 Subject: [PATCH 038/164] Check for pictshare status ok. --- ui/src/components/post-listing.tsx | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index 56c1f0d9c..ef1dfd603 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -818,12 +818,19 @@ export class PostListing extends Component { ) .then(res => res.json()) .then(res => { - let url = `${window.location.origin}/pictshare/${res.url}`; - if (res.filetype == 'mp4') { - url += '/raw'; + if (res.status == 'ok') { + let url = `${window.location.origin}/pictshare/${res.url}`; + if (res.filetype == 'mp4') { + url += '/raw'; + } + this.state.thumbnail = url; + this.setState(this.state); + } else { + console.error( + `Couldn't cache pictshare url: ${this.state.iframely.thumbnail_url}` + ); + console.error(res); } - this.state.thumbnail = url; - this.setState(this.state); }); } }) From eead117d6b54298f86daa161be752096113fa1a7 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Thu, 27 Feb 2020 15:00:41 -0500 Subject: [PATCH 039/164] Fix image testing regex. --- ui/src/components/post-listing.tsx | 4 ++-- ui/src/utils.ts | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index ef1dfd603..8dbe1f48f 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -187,7 +187,7 @@ export class PostListing extends Component { onClick={linkEvent(this, this.handleImageExpandClick)} > {this.imgThumbnail()} - + @@ -199,7 +199,7 @@ export class PostListing extends Component { title={this.state.url} > {this.imgThumbnail()} - + diff --git a/ui/src/utils.ts b/ui/src/utils.ts index d329dcd58..9d2798efe 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -159,10 +159,10 @@ export function isMod(modIds: Array, creator_id: number): boolean { return modIds.includes(creator_id); } -var imageRegex = new RegExp( - `(http)?s?:?(\/\/[^"']*\.(?:png|jpg|jpeg|gif|png|svg))` +const imageRegex = new RegExp( + /(http)?s?:?(\/\/[^"']*\.(?:jpg|jpeg|gif|png|svg))/ ); -var videoRegex = new RegExp(`(http)?s?:?(\/\/[^"']*\.(?:mp4))`); +const videoRegex = new RegExp(`(http)?s?:?(\/\/[^"']*\.(?:mp4))`); export function isImage(url: string) { return imageRegex.test(url); From 3d649756eca377d20de0c47c9cc50d0d76d4b4ba Mon Sep 17 00:00:00 2001 From: Dessalines Date: Thu, 27 Feb 2020 16:24:27 -0500 Subject: [PATCH 040/164] New nsfw posts. --- ui/src/components/main.tsx | 16 +++++++++++++--- ui/src/components/post-listing.tsx | 1 - 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/ui/src/components/main.tsx b/ui/src/components/main.tsx index 87a2fb66b..816120096 100644 --- a/ui/src/components/main.tsx +++ b/ui/src/components/main.tsx @@ -626,10 +626,20 @@ export class Main extends Component { this.state.posts.unshift(data.post); } } else { - this.state.posts.unshift(data.post); - } + // NSFW posts + let nsfw = data.post.nsfw || data.post.community_nsfw; - this.setState(this.state); + // Don't push the post if its nsfw, and don't have that setting on + if ( + !nsfw || + (nsfw && + UserService.Instance.user && + UserService.Instance.user.show_nsfw) + ) { + this.state.posts.unshift(data.post); + this.setState(this.state); + } + } } else if (res.op == UserOperation.EditPost) { let data = res.data as PostResponse; editPostFindRes(data, this.state.posts); diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index 8dbe1f48f..3e6e17eb3 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -846,7 +846,6 @@ export class PostListing extends Component { } else { this.state.thumbnail = null; } - this.setState(this.state); } handlePostLike(i: PostListing) { From 70040e186e454499302a85d3ae0e4789866b030c Mon Sep 17 00:00:00 2001 From: Dessalines Date: Fri, 28 Feb 2020 13:03:16 -0500 Subject: [PATCH 041/164] Adding thumbnail class. --- ui/src/components/post-listing.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index 3e6e17eb3..a77021584 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -286,7 +286,7 @@ export class PostListing extends Component { class="img-fluid img-expanded" data={this.state.thumbnail} > - + From da7429f0f6e2a02fea8324c238eed15691ae85ad Mon Sep 17 00:00:00 2001 From: Dessalines Date: Fri, 28 Feb 2020 14:40:12 -0500 Subject: [PATCH 042/164] Add line for private messaging support. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 81eceb74a..b1931fac3 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,7 @@ Each lemmy server can set its own moderation policy; appointing site-wide admins - A post can consist of a title and any combination of self text, a URL, or nothing else. - Notifications, on comment replies and when you're tagged. - Notifications can be sent via email. + - Private messaging support. - i18n / internationalization support. - RSS / Atom feeds for `All`, `Subscribed`, `Inbox`, `User`, and `Community`. - Cross-posting support. From b854d8f3a0d484fd08bedc75e237fcc2c5eb4a67 Mon Sep 17 00:00:00 2001 From: Felix Date: Sat, 29 Feb 2020 03:11:39 +0100 Subject: [PATCH 043/164] Some federation improvements --- .gitignore | 1 + docker/federation-test/docker-compose.yml | 40 ++++++----------------- server/src/apub/community.rs | 12 ++++--- server/src/apub/puller.rs | 16 +++++---- 4 files changed, 28 insertions(+), 41 deletions(-) diff --git a/.gitignore b/.gitignore index 90972df6a..3eedf03ca 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,6 @@ ansible/inventory_dev ansible/passwords/ docker/lemmy_mine.hjson docker/dev/env_deploy.sh +docker/federation-test/volumes build/ .idea/ diff --git a/docker/federation-test/docker-compose.yml b/docker/federation-test/docker-compose.yml index 2a8b0fc32..39079d106 100644 --- a/docker/federation-test/docker-compose.yml +++ b/docker/federation-test/docker-compose.yml @@ -7,7 +7,7 @@ services: - "127.0.0.1:8540:8540" environment: - LEMMY_HOSTNAME=localhost:8540 - - LEMMY_DATABASE_URL=postgres://lemmy:password@lemmy_db_alpha:5432/lemmy + - LEMMY_DATABASE_URL=postgres://lemmy:password@postgres_alpha:5432/lemmy - LEMMY_JWT_SECRET=changeme - LEMMY_FRONT_END_DIR=/app/dist - LEMMY_FEDERATION_ENABLED=true @@ -16,23 +16,16 @@ services: - RUST_BACKTRACE=1 restart: always depends_on: - - lemmy_db_alpha - lemmy_db_alpha: + - postgres_alpha + postgres_alpha: image: postgres:12-alpine environment: - POSTGRES_USER=lemmy - - POSTGRES_PASSWORD=${LEMMY_DATABASE_PASSWORD} + - POSTGRES_PASSWORD=password - POSTGRES_DB=lemmy volumes: - - lemmy_db_alpha:/var/lib/postgresql/data + - ./volumes/postgres_alpha:/var/lib/postgresql/data restart: always - # lemmy_pictshare_alpha: - # image: shtripok/pictshare:latest - # ports: - # - "127.0.0.1:8550:80" - # volumes: - # - lemmy_pictshare_alpha:/usr/share/nginx/html/data - # restart: always lemmy_beta: image: lemmy-federation-test:latest @@ -40,7 +33,7 @@ services: - "127.0.0.1:8541:8541" environment: - LEMMY_HOSTNAME=localhost:8541 - - LEMMY_DATABASE_URL=postgres://lemmy:password@lemmy_db_beta:5432/lemmy + - LEMMY_DATABASE_URL=postgres://lemmy:password@postgres_beta:5432/lemmy - LEMMY_JWT_SECRET=changeme - LEMMY_FRONT_END_DIR=/app/dist - LEMMY_FEDERATION_ENABLED=true @@ -49,26 +42,13 @@ services: - RUST_BACKTRACE=1 restart: always depends_on: - - lemmy_db_beta - lemmy_db_beta: + - postgres_beta + postgres_beta: image: postgres:12-alpine environment: - POSTGRES_USER=lemmy - - POSTGRES_PASSWORD=${LEMMY_DATABASE_PASSWORD} + - POSTGRES_PASSWORD=password - POSTGRES_DB=lemmy volumes: - - lemmy_db_beta:/var/lib/postgresql/data + - ./volumes/postgres_beta:/var/lib/postgresql/data restart: always - # lemmy_pictshare_beta: - # image: shtripok/pictshare:latest - # ports: - # - "127.0.0.1:8551:80" - # volumes: - # - lemmy_pictshare_beta:/usr/share/nginx/html/data - # restart: always - -volumes: - lemmy_db_alpha: - # lemmy_pictshare_alpha: - lemmy_db_beta: - # lemmy_pictshare_beta: diff --git a/server/src/apub/community.rs b/server/src/apub/community.rs index 32f14eeb2..621d41021 100644 --- a/server/src/apub/community.rs +++ b/server/src/apub/community.rs @@ -8,6 +8,7 @@ use actix_web::body::Body; use actix_web::web::Path; use actix_web::HttpResponse; use serde::Deserialize; +use serde_json::json; impl Community { pub fn as_group(&self) -> Group { @@ -15,16 +16,18 @@ impl Community { let mut group = Group::default(); + // TODO: why the hell is this code so awkward? group.object_props.set_context_object(context()).ok(); - group.object_props.set_id_string(base_url.to_string()).ok(); + group.object_props.set_id_string(self.id.to_string()).ok(); group .object_props - .set_name_string(self.name.to_owned()) + .set_name_string(self.title.to_owned()) .ok(); group .object_props .set_published_utctime(to_datetime_utc(self.published)) .ok(); + group.object_props.attributed_to = Some(json!(self.creator_id.to_string())); if let Some(updated) = self.updated { group .object_props @@ -34,9 +37,7 @@ impl Community { if let Some(description) = &self.description { group - .object_props - .set_summary_string(description.to_string()) - .ok(); + .object_props.summary = Some(json!(description.to_string())); } group @@ -66,6 +67,7 @@ impl Community { //As we are an object, we validated that the community id was valid let community_followers = CommunityFollowerView::for_community(&connection, self.id).unwrap(); + // TODO: we definitely dont want to make our follower list public, we should only expose the count let ap_followers = community_followers .iter() .map(|follower| make_apub_endpoint("u", &follower.user_name)) diff --git a/server/src/apub/puller.rs b/server/src/apub/puller.rs index 4b899a319..b6647060e 100644 --- a/server/src/apub/puller.rs +++ b/server/src/apub/puller.rs @@ -38,6 +38,7 @@ pub fn get_remote_community(identifier: String) -> Result Result().unwrap(), + name: name, + title: community.object_props.name.unwrap().as_str().unwrap().to_string(), // TODO: why does it still show !main@lemmy_beta:8541 + description: community.object_props.summary.map(|c| c.to_string()), // TODO: this has an extra quote somehow category_id: -1, - creator_id: -1, + creator_id: community.object_props.attributed_to.unwrap().as_str().unwrap().parse::().unwrap(), removed: false, - published: naive_now(), // TODO: community.object_props.published + published: naive_now(), // TODO: need to handle time conversion (or handle it in apub lib) updated: Some(naive_now()), // TODO: community.object_props.updated deleted: false, nsfw: false, From 7cdf167e4b28b08e9df90e874be31891ca904f26 Mon Sep 17 00:00:00 2001 From: Felix Date: Sat, 29 Feb 2020 12:42:44 +0100 Subject: [PATCH 044/164] pull in activitypub library --- README.md | 6 + server/Cargo.lock | 5 + server/Cargo.toml | 10 +- server/src/activitypub/activity.rs | 1496 +++++++++++++++++++ server/src/activitypub/actor/mod.rs | 291 ++++ server/src/activitypub/actor/properties.rs | 129 ++ server/src/activitypub/collection.rs | 264 ++++ server/src/activitypub/endpoint.rs | 107 ++ server/src/activitypub/link.rs | 23 + server/src/activitypub/mod.rs | 57 + server/src/activitypub/object/mod.rs | 444 ++++++ server/src/activitypub/object/properties.rs | 111 ++ server/src/apub/community.rs | 5 +- server/src/apub/post.rs | 2 +- server/src/apub/puller.rs | 32 +- server/src/apub/user.rs | 2 +- server/src/lib.rs | 1 + 17 files changed, 2973 insertions(+), 12 deletions(-) create mode 100644 server/src/activitypub/activity.rs create mode 100644 server/src/activitypub/actor/mod.rs create mode 100644 server/src/activitypub/actor/properties.rs create mode 100644 server/src/activitypub/collection.rs create mode 100644 server/src/activitypub/endpoint.rs create mode 100644 server/src/activitypub/link.rs create mode 100644 server/src/activitypub/mod.rs create mode 100644 server/src/activitypub/object/mod.rs create mode 100644 server/src/activitypub/object/properties.rs diff --git a/README.md b/README.md index 81eceb74a..fb99d301a 100644 --- a/README.md +++ b/README.md @@ -165,3 +165,9 @@ ts-node translation_report.ts ## Credits Logo made by Andy Cuccaro (@andycuccaro) under the CC-BY-SA 4.0 license. + +## License + +All code is licensed under AGPLv3 unless otherwise indicated. + +The code in `server/src/activitypub` is taken from the [Aardwolf/activitypub](https://crates.io/crates/activitypub) crate and licensed under GPLv3. diff --git a/server/Cargo.lock b/server/Cargo.lock index 125319206..242fa7893 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -1493,11 +1493,15 @@ name = "lemmy_server" version = "0.0.1" dependencies = [ "activitypub 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "activitystreams-derive 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "activitystreams-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "activitystreams-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "actix 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "actix-files 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "actix-rt 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "actix-web 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "actix-web-actors 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", "bcrypt 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "config 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1517,6 +1521,7 @@ dependencies = [ "reqwest 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)", "rss 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "strum 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/server/Cargo.toml b/server/Cargo.toml index 828230572..1b6ea3053 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -13,6 +13,7 @@ activitypub = "0.2.0" chrono = { version = "0.4.7", features = ["serde"] } failure = "0.1.5" serde_json = { version = "1.0.45", features = ["preserve_order"]} +serde_derive = "1.0" serde = { version = "1.0.94", features = ["derive"] } actix = "0.9.0" actix-web = "2.0.0" @@ -33,4 +34,11 @@ rss = "1.9.0" htmlescape = "0.3.1" config = "0.10.1" hjson = "0.8.2" -reqwest = "0.9.24" \ No newline at end of file +reqwest = "0.9.24" +activitystreams-derive = "0.2" +activitystreams-traits = "0.2" +activitystreams-types = "0.3" + + +[dev-dependencies] +anyhow = "1.0" diff --git a/server/src/activitypub/activity.rs b/server/src/activitypub/activity.rs new file mode 100644 index 000000000..c92b6eaf7 --- /dev/null +++ b/server/src/activitypub/activity.rs @@ -0,0 +1,1496 @@ +/* + * This file is part of ActivityPub. + * + * Copyright © 2018 Riley Trautman + * + * ActivityPub is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ActivityPub is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ActivityPub. If not, see . + */ + +//! Activity traits and types + +pub use activitystreams_traits::{Activity, IntransitiveActivity}; +pub use activitystreams_types::activity::{kind, properties, ActivityExt}; +use serde_derive::{Deserialize, Serialize}; + +use self::{kind::*, properties::*}; +use activitypub::object::{ + properties::{ApObjectProperties, ObjectProperties}, + ApObjectExt, Object, ObjectExt, +}; + +/// Indicates that the actor accepts the object. +/// +/// The target property can be used in certain circumstances to indicate the context into which the +/// object has been accepted. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Accept { + #[serde(rename = "type")] + kind: AcceptType, + + #[serde(flatten)] + pub accept_props: AcceptProperties, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + #[serde(flatten)] + pub activity_props: ActivityProperties, +} + +impl Object for Accept {} +impl ObjectExt for Accept { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Accept { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Activity for Accept {} +impl ActivityExt for Accept { + fn props(&self) -> &ActivityProperties { + &self.activity_props + } + + fn props_mut(&mut self) -> &mut ActivityProperties { + &mut self.activity_props + } +} + +/// Indicates that the actor has added the object to the target. +/// +/// If the target property is not explicitly specified, the target would need to be determined +/// implicitly by context. The origin can be used to identify the context from which the object +/// originated. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Add { + #[serde(rename = "type")] + kind: AddType, + + #[serde(flatten)] + pub add_props: AddProperties, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + #[serde(flatten)] + pub activity_props: ActivityProperties, +} + +impl Object for Add {} +impl ObjectExt for Add { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Add { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Activity for Add {} +impl ActivityExt for Add { + fn props(&self) -> &ActivityProperties { + &self.activity_props + } + + fn props_mut(&mut self) -> &mut ActivityProperties { + &mut self.activity_props + } +} + +/// Indicates that the actor has moved object from origin to target. +/// +/// If the origin or target are not specified, either can be determined by context. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct AMove { + #[serde(rename = "type")] + kind: MoveType, + + #[serde(flatten)] + pub move_props: MoveProperties, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + #[serde(flatten)] + pub activity_props: ActivityProperties, +} + +impl Object for AMove {} +impl ObjectExt for AMove { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for AMove { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Activity for AMove {} +impl ActivityExt for AMove { + fn props(&self) -> &ActivityProperties { + &self.activity_props + } + + fn props_mut(&mut self) -> &mut ActivityProperties { + &mut self.activity_props + } +} + +/// Indicates that the actor is calling the target's attention the object. +/// +/// The origin typically has no defined meaning. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Announce { + #[serde(rename = "type")] + kind: AnnounceType, + + #[serde(flatten)] + pub announce_props: AnnounceProperties, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + #[serde(flatten)] + pub activity_props: ActivityProperties, +} + +impl Object for Announce {} +impl ObjectExt for Announce { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Announce { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Activity for Announce {} +impl ActivityExt for Announce { + fn props(&self) -> &ActivityProperties { + &self.activity_props + } + + fn props_mut(&mut self) -> &mut ActivityProperties { + &mut self.activity_props + } +} + +/// An IntransitiveActivity that indicates that the actor has arrived at the location. +/// +/// The origin can be used to identify the context from which the actor originated. The target +/// typically has no defined meaning. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Arrive { + #[serde(rename = "type")] + kind: ArriveType, + + #[serde(flatten)] + pub arrive_props: ArriveProperties, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + #[serde(flatten)] + pub activity_props: ActivityProperties, +} + +impl Object for Arrive {} +impl ObjectExt for Arrive { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Arrive { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Activity for Arrive {} +impl ActivityExt for Arrive { + fn props(&self) -> &ActivityProperties { + &self.activity_props + } + + fn props_mut(&mut self) -> &mut ActivityProperties { + &mut self.activity_props + } +} +impl IntransitiveActivity for Arrive {} + +/// Indicates that the actor is blocking the object. +/// +/// Blocking is a stronger form of Ignore. The typical use is to support social systems that allow +/// one user to block activities or content of other users. The target and origin typically have no +/// defined meaning. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Block { + #[serde(rename = "type")] + kind: BlockType, + + #[serde(flatten)] + pub block_props: BlockProperties, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + #[serde(flatten)] + pub activity_props: ActivityProperties, +} + +impl Object for Block {} +impl ObjectExt for Block { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Block { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Activity for Block {} +impl ActivityExt for Block { + fn props(&self) -> &ActivityProperties { + &self.activity_props + } + + fn props_mut(&mut self) -> &mut ActivityProperties { + &mut self.activity_props + } +} + +/// Indicates that the actor has created the object. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Create { + #[serde(rename = "type")] + kind: CreateType, + + #[serde(flatten)] + pub create_props: CreateProperties, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + #[serde(flatten)] + pub activity_props: ActivityProperties, +} + +impl Object for Create {} +impl ObjectExt for Create { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Create { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Activity for Create {} +impl ActivityExt for Create { + fn props(&self) -> &ActivityProperties { + &self.activity_props + } + + fn props_mut(&mut self) -> &mut ActivityProperties { + &mut self.activity_props + } +} + +/// Indicates that the actor has deleted the object. +/// +/// If specified, the origin indicates the context from which the object was deleted. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Delete { + #[serde(rename = "type")] + kind: DeleteType, + + #[serde(flatten)] + pub delete_props: DeleteProperties, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + #[serde(flatten)] + pub activity_props: ActivityProperties, +} + +impl Object for Delete {} +impl ObjectExt for Delete { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Delete { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Activity for Delete {} +impl ActivityExt for Delete { + fn props(&self) -> &ActivityProperties { + &self.activity_props + } + + fn props_mut(&mut self) -> &mut ActivityProperties { + &mut self.activity_props + } +} + +/// Indicates that the actor dislikes the object. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Dislike { + #[serde(rename = "type")] + kind: DislikeType, + + #[serde(flatten)] + pub dislike_props: DislikeProperties, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + #[serde(flatten)] + pub activity_props: ActivityProperties, +} + +impl Object for Dislike {} +impl ObjectExt for Dislike { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Dislike { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Activity for Dislike {} +impl ActivityExt for Dislike { + fn props(&self) -> &ActivityProperties { + &self.activity_props + } + + fn props_mut(&mut self) -> &mut ActivityProperties { + &mut self.activity_props + } +} + +/// Indicates that the actor is "flagging" the object. +/// +/// Flagging is defined in the sense common to many social platforms as reporting content as being +/// inappropriate for any number of reasons. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Flag { + #[serde(rename = "type")] + kind: FlagType, + + #[serde(flatten)] + pub flag_props: FlagProperties, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + #[serde(flatten)] + pub activity_props: ActivityProperties, +} + +impl Object for Flag {} +impl ObjectExt for Flag { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Flag { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Activity for Flag {} +impl ActivityExt for Flag { + fn props(&self) -> &ActivityProperties { + &self.activity_props + } + + fn props_mut(&mut self) -> &mut ActivityProperties { + &mut self.activity_props + } +} + +/// Indicates that the actor is "following" the object. +/// +/// Following is defined in the sense typically used within Social systems in which the actor is +/// interested in any activity performed by or on the object. The target and origin typically have +/// no defined meaning. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Follow { + #[serde(rename = "type")] + kind: FollowType, + + #[serde(flatten)] + pub follow_props: FollowProperties, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + #[serde(flatten)] + pub activity_props: ActivityProperties, +} + +impl Object for Follow {} +impl ObjectExt for Follow { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Follow { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Activity for Follow {} +impl ActivityExt for Follow { + fn props(&self) -> &ActivityProperties { + &self.activity_props + } + + fn props_mut(&mut self) -> &mut ActivityProperties { + &mut self.activity_props + } +} + +/// Indicates that the actor is ignoring the object. +/// +/// The target and origin typically have no defined meaning. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Ignore { + #[serde(rename = "type")] + kind: IgnoreType, + + #[serde(flatten)] + pub ignore_props: IgnoreProperties, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + #[serde(flatten)] + pub activity_props: ActivityProperties, +} + +impl Object for Ignore {} +impl ObjectExt for Ignore { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Ignore { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Activity for Ignore {} +impl ActivityExt for Ignore { + fn props(&self) -> &ActivityProperties { + &self.activity_props + } + + fn props_mut(&mut self) -> &mut ActivityProperties { + &mut self.activity_props + } +} + +/// A specialization of Offer in which the actor is extending an invitation for the object to the +/// target. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Invite { + #[serde(rename = "type")] + kind: InviteType, + + #[serde(flatten)] + pub invite_props: InviteProperties, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + #[serde(flatten)] + pub activity_props: ActivityProperties, +} + +impl Object for Invite {} +impl ObjectExt for Invite { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Invite { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Activity for Invite {} +impl ActivityExt for Invite { + fn props(&self) -> &ActivityProperties { + &self.activity_props + } + + fn props_mut(&mut self) -> &mut ActivityProperties { + &mut self.activity_props + } +} + +/// Indicates that the actor has joined the object. +/// +/// The target and origin typically have no defined meaning +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Join { + #[serde(rename = "type")] + kind: JoinType, + + #[serde(flatten)] + pub join_props: JoinProperties, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + #[serde(flatten)] + pub activity_props: ActivityProperties, +} + +impl Object for Join {} +impl ObjectExt for Join { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Join { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Activity for Join {} +impl ActivityExt for Join { + fn props(&self) -> &ActivityProperties { + &self.activity_props + } + + fn props_mut(&mut self) -> &mut ActivityProperties { + &mut self.activity_props + } +} + +/// Indicates that the actor has left the object. +/// +/// The target and origin typically have no meaning. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Leave { + #[serde(rename = "type")] + kind: LeaveType, + + #[serde(flatten)] + pub leave_props: LeaveProperties, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + #[serde(flatten)] + pub activity_props: ActivityProperties, +} + +impl Object for Leave {} +impl ObjectExt for Leave { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Leave { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Activity for Leave {} +impl ActivityExt for Leave { + fn props(&self) -> &ActivityProperties { + &self.activity_props + } + + fn props_mut(&mut self) -> &mut ActivityProperties { + &mut self.activity_props + } +} + +/// Indicates that the actor likes, recommends or endorses the object. +/// +/// The target and origin typically have no defined meaning. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Like { + #[serde(rename = "type")] + kind: LikeType, + + #[serde(flatten)] + pub like_props: LikeProperties, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + #[serde(flatten)] + pub activity_props: ActivityProperties, +} + +impl Object for Like {} +impl ObjectExt for Like { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Like { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Activity for Like {} +impl ActivityExt for Like { + fn props(&self) -> &ActivityProperties { + &self.activity_props + } + + fn props_mut(&mut self) -> &mut ActivityProperties { + &mut self.activity_props + } +} + +/// Indicates that the actor has listened to the object. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Listen { + #[serde(rename = "type")] + kind: ListenType, + + #[serde(flatten)] + pub listen_props: ListenProperties, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + #[serde(flatten)] + pub activity_props: ActivityProperties, +} + +impl Object for Listen {} +impl ObjectExt for Listen { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Listen { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Activity for Listen {} +impl ActivityExt for Listen { + fn props(&self) -> &ActivityProperties { + &self.activity_props + } + + fn props_mut(&mut self) -> &mut ActivityProperties { + &mut self.activity_props + } +} + +/// Indicates that the actor is offering the object. +/// +/// If specified, the target indicates the entity to which the object is being offered. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Offer { + #[serde(rename = "type")] + kind: OfferType, + + #[serde(flatten)] + pub offer_props: OfferProperties, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + #[serde(flatten)] + pub activity_props: ActivityProperties, +} + +impl Object for Offer {} +impl ObjectExt for Offer { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Offer { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Activity for Offer {} +impl ActivityExt for Offer { + fn props(&self) -> &ActivityProperties { + &self.activity_props + } + + fn props_mut(&mut self) -> &mut ActivityProperties { + &mut self.activity_props + } +} + +/// Represents a question being asked. +/// +/// Question objects are an extension of IntransitiveActivity. That is, the Question object is an +/// Activity, but the direct object is the question itself and therefore it would not contain an +/// object property. +/// +/// Either of the anyOf and oneOf properties MAY be used to express possible answers, but a +/// Question object MUST NOT have both properties. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Question { + #[serde(rename = "type")] + kind: QuestionType, + + #[serde(flatten)] + pub question_props: QuestionProperties, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + #[serde(flatten)] + pub activity_props: ActivityProperties, +} + +impl Object for Question {} +impl ObjectExt for Question { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Question { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Activity for Question {} +impl ActivityExt for Question { + fn props(&self) -> &ActivityProperties { + &self.activity_props + } + + fn props_mut(&mut self) -> &mut ActivityProperties { + &mut self.activity_props + } +} +impl IntransitiveActivity for Question {} + +/// Indicates that the actor has read the object. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Read { + #[serde(rename = "type")] + kind: ReadType, + + #[serde(flatten)] + pub read_props: ReadProperties, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + #[serde(flatten)] + pub activity_props: ActivityProperties, +} + +impl Object for Read {} +impl ObjectExt for Read { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Read { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Activity for Read {} +impl ActivityExt for Read { + fn props(&self) -> &ActivityProperties { + &self.activity_props + } + + fn props_mut(&mut self) -> &mut ActivityProperties { + &mut self.activity_props + } +} + +/// Indicates that the actor is rejecting the object. +/// +/// The target and origin typically have no defined meaning. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Reject { + #[serde(rename = "type")] + kind: RejectType, + + #[serde(flatten)] + pub reject_props: RejectProperties, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + #[serde(flatten)] + pub activity_props: ActivityProperties, +} + +impl Object for Reject {} +impl ObjectExt for Reject { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Reject { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Activity for Reject {} +impl ActivityExt for Reject { + fn props(&self) -> &ActivityProperties { + &self.activity_props + } + + fn props_mut(&mut self) -> &mut ActivityProperties { + &mut self.activity_props + } +} + +/// Indicates that the actor is removing the object. +/// +/// If specified, the origin indicates the context from which the object is being removed. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Remove { + #[serde(rename = "type")] + kind: RemoveType, + + #[serde(flatten)] + pub remove_props: RemoveProperties, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + #[serde(flatten)] + pub activity_props: ActivityProperties, +} + +impl Object for Remove {} +impl ObjectExt for Remove { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Remove { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Activity for Remove {} +impl ActivityExt for Remove { + fn props(&self) -> &ActivityProperties { + &self.activity_props + } + + fn props_mut(&mut self) -> &mut ActivityProperties { + &mut self.activity_props + } +} + +/// A specialization of Accept indicating that the acceptance is tentative. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct TentativeAccept { + #[serde(rename = "type")] + kind: TentativeAcceptType, + + #[serde(flatten)] + pub tentative_accept_props: TentativeAcceptProperties, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + #[serde(flatten)] + pub activity_props: ActivityProperties, +} + +impl Object for TentativeAccept {} +impl ObjectExt for TentativeAccept { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for TentativeAccept { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Activity for TentativeAccept {} +impl ActivityExt for TentativeAccept { + fn props(&self) -> &ActivityProperties { + &self.activity_props + } + + fn props_mut(&mut self) -> &mut ActivityProperties { + &mut self.activity_props + } +} + +/// A specialization of Reject in which the rejection is considered tentative. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct TentativeReject { + #[serde(rename = "type")] + kind: TentativeRejectType, + + #[serde(flatten)] + pub tentative_reject_props: TentativeRejectProperties, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + #[serde(flatten)] + pub activity_props: ActivityProperties, +} + +impl Object for TentativeReject {} +impl ObjectExt for TentativeReject { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for TentativeReject { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Activity for TentativeReject {} +impl ActivityExt for TentativeReject { + fn props(&self) -> &ActivityProperties { + &self.activity_props + } + + fn props_mut(&mut self) -> &mut ActivityProperties { + &mut self.activity_props + } +} + +/// Indicates that the actor is traveling to target from origin. +/// +/// Travel is an IntransitiveObject whose actor specifies the direct object. If the target or +/// origin are not specified, either can be determined by context. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Travel { + #[serde(rename = "type")] + kind: TravelType, + + #[serde(flatten)] + pub travel_props: TravelProperties, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + #[serde(flatten)] + pub activity_props: ActivityProperties, +} + +impl Object for Travel {} +impl ObjectExt for Travel { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Travel { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Activity for Travel {} +impl ActivityExt for Travel { + fn props(&self) -> &ActivityProperties { + &self.activity_props + } + + fn props_mut(&mut self) -> &mut ActivityProperties { + &mut self.activity_props + } +} +impl IntransitiveActivity for Travel {} + +/// Indicates that the actor is undoing the object. +/// +/// In most cases, the object will be an Activity describing some previously performed action (for +/// instance, a person may have previously "liked" an article but, for whatever reason, might +/// choose to undo that like at some later point in time). +/// +/// The target and origin typically have no defined meaning. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Undo { + #[serde(rename = "type")] + kind: UndoType, + + #[serde(flatten)] + pub undo_props: UndoProperties, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + #[serde(flatten)] + pub activity_props: ActivityProperties, +} + +impl Object for Undo {} +impl ObjectExt for Undo { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Undo { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Activity for Undo {} +impl ActivityExt for Undo { + fn props(&self) -> &ActivityProperties { + &self.activity_props + } + + fn props_mut(&mut self) -> &mut ActivityProperties { + &mut self.activity_props + } +} + +/// Indicates that the actor has updated the object. +/// +/// Note, however, that this vocabulary does not define a mechanism for describing the actual set +/// of modifications made to object. +/// +/// The target and origin typically have no defined meaning. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Update { + #[serde(rename = "type")] + kind: UpdateType, + + #[serde(flatten)] + pub update_props: UpdateProperties, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + #[serde(flatten)] + pub activity_props: ActivityProperties, +} + +impl Object for Update {} +impl ObjectExt for Update { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Update { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Activity for Update {} +impl ActivityExt for Update { + fn props(&self) -> &ActivityProperties { + &self.activity_props + } + + fn props_mut(&mut self) -> &mut ActivityProperties { + &mut self.activity_props + } +} + +/// Indicates that the actor has viewed the object. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct View { + #[serde(rename = "type")] + kind: ViewType, + + #[serde(flatten)] + pub view_props: ViewProperties, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + #[serde(flatten)] + pub activity_props: ActivityProperties, +} + +impl Object for View {} +impl ObjectExt for View { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for View { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Activity for View {} +impl ActivityExt for View { + fn props(&self) -> &ActivityProperties { + &self.activity_props + } + + fn props_mut(&mut self) -> &mut ActivityProperties { + &mut self.activity_props + } +} diff --git a/server/src/activitypub/actor/mod.rs b/server/src/activitypub/actor/mod.rs new file mode 100644 index 000000000..adb0b2d5c --- /dev/null +++ b/server/src/activitypub/actor/mod.rs @@ -0,0 +1,291 @@ +/* + * This file is part of ActivityPub. + * + * Copyright © 2018 Riley Trautman + * + * ActivityPub is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ActivityPub is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ActivityPub. If not, see . + */ + +//! Actor traits and types + +use activitystreams_derive::Properties; +pub use activitystreams_traits::Actor; +pub use activitystreams_types::actor::kind; +use serde_derive::{Deserialize, Serialize}; + +pub mod properties; + +use self::{kind::*, properties::*}; +use activitypub::object::{ + properties::{ApObjectProperties, ObjectProperties}, + ApObjectExt, Object, ObjectExt, +}; + +/// The ActivityPub Actor Extension Trait +/// +/// This trait provides generic access to an activitypub actor's properties +pub trait ApActorExt: Actor { + fn props(&self) -> &ApActorProperties; + fn props_mut(&mut self) -> &mut ApActorProperties; +} + +/// Describes a software application. +#[derive(Clone, Debug, Default, Deserialize, Serialize, Properties)] +#[serde(rename_all = "camelCase")] +pub struct Application { + #[serde(rename = "type")] + kind: ApplicationType, + + /// Adds all valid object properties to this struct + #[serde(flatten)] + pub object_props: ObjectProperties, + + /// Adds all valid activitypub object properties to this struct + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + /// Adds all valid activitypub actor properties to this struct + #[serde(flatten)] + pub ap_actor_props: ApActorProperties, +} + +impl Object for Application {} +impl ObjectExt for Application { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Application { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Actor for Application {} +impl ApActorExt for Application { + fn props(&self) -> &ApActorProperties { + &self.ap_actor_props + } + + fn props_mut(&mut self) -> &mut ApActorProperties { + &mut self.ap_actor_props + } +} + +/// Represents a formal or informal collective of Actors. +#[derive(Clone, Debug, Default, Deserialize, Serialize, Properties)] +#[serde(rename_all = "camelCase")] +pub struct Group { + #[serde(rename = "type")] + kind: GroupType, + + /// Adds all valid object properties to this struct + #[serde(flatten)] + pub object_props: ObjectProperties, + + /// Adds all valid activitypub object properties to this struct + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + /// Adds all valid activitypub actor properties to this struct + #[serde(flatten)] + pub ap_actor_props: ApActorProperties, +} + +impl Object for Group {} +impl ObjectExt for Group { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Group { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Actor for Group {} +impl ApActorExt for Group { + fn props(&self) -> &ApActorProperties { + &self.ap_actor_props + } + + fn props_mut(&mut self) -> &mut ApActorProperties { + &mut self.ap_actor_props + } +} + +/// Represents an organization. +#[derive(Clone, Debug, Default, Deserialize, Serialize, Properties)] +#[serde(rename_all = "camelCase")] +pub struct Organization { + #[serde(rename = "type")] + kind: OrganizationType, + + /// Adds all valid object properties to this struct + #[serde(flatten)] + pub object_props: ObjectProperties, + + /// Adds all valid activitypub object properties to this struct + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + /// Adds all valid activitypub actor properties to this struct + #[serde(flatten)] + pub ap_actor_props: ApActorProperties, +} + +impl Object for Organization {} +impl ObjectExt for Organization { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Organization { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Actor for Organization {} +impl ApActorExt for Organization { + fn props(&self) -> &ApActorProperties { + &self.ap_actor_props + } + + fn props_mut(&mut self) -> &mut ApActorProperties { + &mut self.ap_actor_props + } +} + +/// Represents an individual person. +#[derive(Clone, Debug, Default, Deserialize, Serialize, Properties)] +#[serde(rename_all = "camelCase")] +pub struct Person { + #[serde(rename = "type")] + kind: PersonType, + + /// Adds all valid object properties to this struct + #[serde(flatten)] + pub object_props: ObjectProperties, + + /// Adds all valid activitypub object properties to this struct + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + /// Adds all valid activitypub actor properties to this struct + #[serde(flatten)] + pub ap_actor_props: ApActorProperties, +} + +impl Object for Person {} +impl ObjectExt for Person { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Person { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Actor for Person {} +impl ApActorExt for Person { + fn props(&self) -> &ApActorProperties { + &self.ap_actor_props + } + + fn props_mut(&mut self) -> &mut ApActorProperties { + &mut self.ap_actor_props + } +} + +/// Represents a service of any kind. +#[derive(Clone, Debug, Default, Deserialize, Serialize, Properties)] +#[serde(rename_all = "camelCase")] +pub struct Service { + #[serde(rename = "type")] + kind: ServiceType, + + /// Adds all valid object properties to this struct + #[serde(flatten)] + pub object_props: ObjectProperties, + + /// Adds all valid activitypub object properties to this struct + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + /// Adds all valid activitypub actor properties to this struct + #[serde(flatten)] + pub ap_actor_props: ApActorProperties, +} + +impl Object for Service {} +impl ObjectExt for Service { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Service { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Actor for Service {} +impl ApActorExt for Service { + fn props(&self) -> &ApActorProperties { + &self.ap_actor_props + } + + fn props_mut(&mut self) -> &mut ApActorProperties { + &mut self.ap_actor_props + } +} diff --git a/server/src/activitypub/actor/properties.rs b/server/src/activitypub/actor/properties.rs new file mode 100644 index 000000000..9b7f38aa7 --- /dev/null +++ b/server/src/activitypub/actor/properties.rs @@ -0,0 +1,129 @@ +/* + * This file is part of ActivityPub. + * + * Copyright © 2018 Riley Trautman + * + * ActivityPub is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ActivityPub is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ActivityPub. If not, see . + */ + +//! Namespace for properties of standard Actor types +//! +//! To use these properties in your own types, you can flatten them into your struct with serde: +//! +//! ```rust +//! use activitypub::{Object, Actor, actor::properties::ApActorProperties}; +//! use serde_derive::{Deserialize, Serialize}; +//! +//! #[derive(Clone, Debug, Serialize, Deserialize)] +//! #[serde(rename_all = "camelCase")] +//! pub struct MyActor { +//! #[serde(rename = "type")] +//! pub kind: String, +//! +//! /// Define a require property for the MyActor type +//! pub my_property: String, +//! +//! #[serde(flatten)] +//! pub actor_props: ApActorProperties, +//! } +//! +//! impl Object for MyActor {} +//! impl Actor for MyActor {} +//! # +//! # fn main() {} +//! ``` + +use activitystreams_derive::Properties; +use serde_derive::{Deserialize, Serialize}; + +use crate::activitypub::endpoint::Endpoint; + +/// Define activitypub properties for the Actor type as described by the Activity Pub vocabulary. +#[derive(Clone, Debug, Default, Deserialize, Properties, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ApActorProperties { + // TODO: IRI + /// A reference to an [[ActivityStreams](https://www.w3.org/ns/activitystreams)] + /// OrderedCollection comprised of all the messages received by the actor. + /// + /// - Range: `anyUri` + /// - Functional: true + #[activitystreams(concrete(String), functional)] + pub inbox: serde_json::Value, + + // TODO: IRI + /// An [ActivityStreams](https://www.w3.org/ns/activitystreams)] OrderedCollection comprised of + /// all the messages produced by the actor. + /// + /// - Range: `anyUri` + /// - Functional: true + #[activitystreams(concrete(String), functional)] + pub outbox: serde_json::Value, + + // TODO: IRI + /// A link to an [[ActivityStreams](https://www.w3.org/ns/activitystreams)] collection of the + /// actors that this actor is following. + /// + /// - Range: `anyUri` + /// - Functional: true + #[activitystreams(concrete(String), functional)] + pub following: Option, + + // TODO: IRI + /// A link to an [[ActivityStreams](https://www.w3.org/ns/activitystreams)] collection of the + /// actors that follow this actor. + /// + /// - Range: `anyUri` + /// - Functional: true + #[activitystreams(concrete(String), functional)] + pub followers: Option, + + // TODO: IRI + /// A link to an [[ActivityStreams](https://www.w3.org/ns/activitystreams)] collection of + /// objects this actor has liked. + /// + /// - Range: `anyUri` + /// - Functional: true + #[activitystreams(concrete(String), functional)] + pub liked: Option, + + // TODO: IRI + /// A list of supplementary Collections which may be of interest. + /// + /// - Range: `anyUri` + /// - Functional: false + #[serde(skip_serializing_if = "Option::is_none")] + #[activitystreams(concrete(String))] + pub streams: Option, + + /// A short username which may be used to refer to the actor, with no uniqueness guarantees. + /// + /// - Range: `anyUri` + /// - Functional: true + #[serde(skip_serializing_if = "Option::is_none")] + #[activitystreams(concrete(String), functional)] + pub preferred_username: Option, + + /// A json object which maps additional (typically server/domain-wide) endpoints which may be + /// useful either for this actor or someone referencing this actor. + /// + /// This mapping may be nested inside the actor document as the value or may be a link to a + /// JSON-LD document with these properties. + /// + /// - Range: `Endpoint` + /// - Functional: true + #[serde(skip_serializing_if = "Option::is_none")] + #[activitystreams(concrete(Endpoint), functional)] + pub endpoints: Option, +} diff --git a/server/src/activitypub/collection.rs b/server/src/activitypub/collection.rs new file mode 100644 index 000000000..8a25cce44 --- /dev/null +++ b/server/src/activitypub/collection.rs @@ -0,0 +1,264 @@ +/* + * This file is part of ActivityPub. + * + * Copyright © 2018 Riley Trautman + * + * ActivityPub is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ActivityPub is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ActivityPub. If not, see . + */ + +//! Collection traits and types + +use activitystreams_derive::Properties; +pub use activitystreams_traits::{Collection, CollectionPage}; +pub use activitystreams_types::collection::{kind, properties, CollectionExt, CollectionPageExt}; +use serde_derive::{Deserialize, Serialize}; + +use self::{kind::*, properties::*}; +use activitypub::object::{ + properties::{ApObjectProperties, ObjectProperties}, + ApObjectExt, Object, ObjectExt, +}; + +/// The default `Collection` type. +#[derive(Clone, Debug, Default, Deserialize, Serialize, Properties)] +#[serde(rename_all = "camelCase")] +pub struct UnorderedCollection { + #[serde(rename = "type")] + kind: CollectionType, + + /// Adds all valid object properties to this struct + #[serde(flatten)] + pub object_props: ObjectProperties, + + /// Adds all valid ap object properties to this struct + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + /// Adds all valid collection properties to this struct + #[serde(flatten)] + pub collection_props: CollectionProperties, +} + +impl Object for UnorderedCollection {} +impl ObjectExt for UnorderedCollection { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for UnorderedCollection { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Collection for UnorderedCollection {} +impl CollectionExt for UnorderedCollection { + fn props(&self) -> &CollectionProperties { + &self.collection_props + } + + fn props_mut(&mut self) -> &mut CollectionProperties { + &mut self.collection_props + } +} + +/// Used to represent distinct subsets of items from a `Collection`. +#[derive(Clone, Debug, Default, Deserialize, Serialize, Properties)] +#[serde(rename_all = "camelCase")] +pub struct UnorderedCollectionPage { + #[serde(rename = "type")] + kind: CollectionPageType, + + /// Adds all valid object properties to this struct + #[serde(flatten)] + pub object_props: ObjectProperties, + + /// Adds all valid ap object properties to this struct + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + /// Adds all valid collection properties to this struct + #[serde(flatten)] + pub collection_props: CollectionProperties, + + /// Adds all valid collection page properties to this struct + #[serde(flatten)] + pub collection_page_props: CollectionPageProperties, +} + +impl Object for UnorderedCollectionPage {} +impl ObjectExt for UnorderedCollectionPage { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for UnorderedCollectionPage { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Collection for UnorderedCollectionPage {} +impl CollectionExt for UnorderedCollectionPage { + fn props(&self) -> &CollectionProperties { + &self.collection_props + } + + fn props_mut(&mut self) -> &mut CollectionProperties { + &mut self.collection_props + } +} +impl CollectionPage for UnorderedCollectionPage {} +impl CollectionPageExt for UnorderedCollectionPage { + fn props(&self) -> &CollectionPageProperties { + &self.collection_page_props + } + + fn props_mut(&mut self) -> &mut CollectionPageProperties { + &mut self.collection_page_props + } +} + +/// A subtype of `Collection` in which members of the logical collection are assumed to always be +/// strictly ordered. +#[derive(Clone, Debug, Default, Deserialize, Serialize, Properties)] +#[serde(rename_all = "camelCase")] +pub struct OrderedCollection { + #[serde(rename = "type")] + kind: OrderedCollectionType, + + /// Adds all valid object properties to this struct + #[serde(flatten)] + pub object_props: ObjectProperties, + + /// Adds all valid ap object properties to this struct + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + /// Adds all valid collection properties to this struct + #[serde(flatten)] + pub collection_props: CollectionProperties, +} + +impl Object for OrderedCollection {} +impl ObjectExt for OrderedCollection { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for OrderedCollection { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Collection for OrderedCollection {} +impl CollectionExt for OrderedCollection { + fn props(&self) -> &CollectionProperties { + &self.collection_props + } + + fn props_mut(&mut self) -> &mut CollectionProperties { + &mut self.collection_props + } +} + +/// Used to represent ordered subsets of items from an `OrderedCollection`. +#[derive(Clone, Debug, Default, Deserialize, Serialize, Properties)] +#[serde(rename_all = "camelCase")] +pub struct OrderedCollectionPage { + #[serde(rename = "type")] + kind: OrderedCollectionPageType, + + /// Adds all valid object properties to this struct + #[serde(flatten)] + pub object_props: ObjectProperties, + + /// Adds all valid ap object properties to this struct + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + /// Adds all valid collection properties to this struct + #[serde(flatten)] + pub collection_props: CollectionProperties, + + /// Adds all valid collection page properties to this struct + #[serde(flatten)] + pub collection_page_props: CollectionPageProperties, + + /// Adds all valid ordered collection page properties to this struct + #[serde(flatten)] + pub ordered_collection_page_props: OrderedCollectionPageProperties, +} + +impl Object for OrderedCollectionPage {} +impl ObjectExt for OrderedCollectionPage { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for OrderedCollectionPage { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} +impl Collection for OrderedCollectionPage {} +impl CollectionExt for OrderedCollectionPage { + fn props(&self) -> &CollectionProperties { + &self.collection_props + } + + fn props_mut(&mut self) -> &mut CollectionProperties { + &mut self.collection_props + } +} +impl CollectionPage for OrderedCollectionPage {} +impl CollectionPageExt for OrderedCollectionPage { + fn props(&self) -> &CollectionPageProperties { + &self.collection_page_props + } + + fn props_mut(&mut self) -> &mut CollectionPageProperties { + &mut self.collection_page_props + } +} diff --git a/server/src/activitypub/endpoint.rs b/server/src/activitypub/endpoint.rs new file mode 100644 index 000000000..639860cf6 --- /dev/null +++ b/server/src/activitypub/endpoint.rs @@ -0,0 +1,107 @@ +/* + * This file is part of ActivityPub. + * + * Copyright © 2018 Riley Trautman + * + * ActivityPub is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ActivityPub is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ActivityPub. If not, see . + */ + +//! Endpoint traits and types + +use activitystreams_derive::Properties; +use serde_derive::{Deserialize, Serialize}; + +/// A json object which maps additional (typically server/domain-wide) endpoints which may be +/// useful either for this actor or someone referencing this actor. +/// +/// This mapping may be nested inside the actor document as the value or may be a link to a JSON-LD +/// document with these properties. +#[derive(Clone, Debug, Default, Deserialize, Properties, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Endpoint { + // TODO: IRI + /// Endpoint URI so this actor's clients may access remote ActivityStreams objects which + /// require authentication to access. + /// + /// To use this endpoint, the client posts an x-www-form-urlencoded id parameter with the value + /// being the id of the requested ActivityStreams object. + /// + /// - Range: `anyUri` + /// - Functional: true + #[serde(skip_serializing_if = "Option::is_none")] + #[activitystreams(concrete(String))] + pub proxy_url: Option, + + // TODO: IRI + /// If OAuth 2.0 bearer tokens [[RFC6749](https://tools.ietf.org/html/rfc6749)] + /// [[RFC6750](https://tools.ietf.org/html/rfc6750)] are being used for authenticating client + /// to server interactions, this endpoint specifies a URI at which a browser-authenticated user + /// may obtain a new authorization grant. + /// + /// - Range: `anyUri` + /// - Functional: true + #[serde(skip_serializing_if = "Option::is_none")] + #[activitystreams(concrete(String))] + pub oauth_authorization_endpoint: Option, + + // TODO: IRI + /// If OAuth 2.0 bearer tokens [[RFC6749](https://tools.ietf.org/html/rfc6749)] + /// [[RFC6750](https://tools.ietf.org/html/rfc6750)] are being used for authenticating client + /// to server interactions, this endpoint specifies a URI at which a client may acquire an + /// access token. + /// + /// - Range: `anyUri` + /// - Functional: true + #[serde(skip_serializing_if = "Option::is_none")] + #[activitystreams(concrete(String))] + pub oauth_token_endpoint: Option, + + // TODO: IRI + /// If Linked Data Signatures and HTTP Signatures are being used for authentication and + /// authorization, this endpoint specifies a URI at which browser-authenticated users may + /// authorize a client's public key for client to server interactions. + /// + /// - Range: `anyUri` + /// - Functional: true + #[serde(skip_serializing_if = "Option::is_none")] + #[activitystreams(concrete(String))] + pub provide_client_key: Option, + + // TODO: IRI + /// If Linked Data Signatures and HTTP Signatures are being used for authentication and + /// authorization, this endpoint specifies a URI at which a client key may be signed by the + /// actor's key for a time window to act on behalf of the actor in interacting with foreign + /// servers. + /// + /// - Range: `anyUri` + /// - Functional: true + #[serde(skip_serializing_if = "Option::is_none")] + #[activitystreams(concrete(String))] + pub sign_client_key: Option, + + // TODO: IRI + /// An optional endpoint used for wide delivery of publicly addressed activities and activities + /// sent to followers. + /// + /// `shared_inbox`endpoints SHOULD also be publicly readable `OrderedCollection` objects + /// containing objects addressed to the Public special collection. Reading from the + /// `shared_inbox` endpoint MUST NOT present objects which are not addressed to the Public + /// endpoint. + /// + /// - Range: `anyUri` + /// - Functional: true + #[serde(skip_serializing_if = "Option::is_none")] + #[activitystreams(concrete(String))] + pub shared_inbox: Option, +} diff --git a/server/src/activitypub/link.rs b/server/src/activitypub/link.rs new file mode 100644 index 000000000..5de09fd88 --- /dev/null +++ b/server/src/activitypub/link.rs @@ -0,0 +1,23 @@ +/* + * This file is part of ActivityPub. + * + * Copyright © 2018 Riley Trautman + * + * ActivityPub is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ActivityPub is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ActivityPub. If not, see . + */ + +//! Link traits and types + +pub use activitystreams_traits::Link; +pub use activitystreams_types::link::{kind, properties, LinkExt, Mention}; diff --git a/server/src/activitypub/mod.rs b/server/src/activitypub/mod.rs new file mode 100644 index 000000000..9e990329c --- /dev/null +++ b/server/src/activitypub/mod.rs @@ -0,0 +1,57 @@ +/* + * This file is part of ActivityPub. + * + * Copyright © 2018 Riley Trautman + * + * ActivityPub is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ActivityPub is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ActivityPub. If not, see . + */ + +//! ActivityPub +//! +//! This crate defines the base set of types from the ActivityPub specification. +//! +//! ## Example Usage +//! ```rust +//! use activitypub::{context, object::Video}; +//! use anyhow::Error; +//! +//! fn run() -> Result<(), Error> { +//! let mut video = Video::default(); +//! video.object_props.set_context_object(context())?; +//! video.ap_object_props.set_likes_string("https://my-instance.com/likes".to_owned()); +//! +//! let video_string = serde_json::to_string(&video)?; +//! +//! let video: Video = serde_json::from_str(&video_string)?; +//! +//! Ok(()) +//! } +//! ``` +pub mod activity; +pub mod actor; +pub mod collection; +mod endpoint; +pub mod link; +pub mod object; + +pub use self::{ + activity::{Activity, IntransitiveActivity}, + actor::Actor, + collection::{Collection, CollectionPage}, + endpoint::Endpoint, + link::Link, + object::Object, +}; +pub use activitystreams_traits::{properties, Error, Result}; +pub use activitystreams_types::{context, ContextObject, CustomLink, CustomObject}; diff --git a/server/src/activitypub/object/mod.rs b/server/src/activitypub/object/mod.rs new file mode 100644 index 000000000..07aa99e80 --- /dev/null +++ b/server/src/activitypub/object/mod.rs @@ -0,0 +1,444 @@ +/* + * This file is part of ActivityPub. + * + * Copyright © 2018 Riley Trautman + * + * ActivityPub is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ActivityPub is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ActivityPub. If not, see . + */ + +//! Object traits and types + +pub use activitystreams_traits::Object; +pub use activitystreams_types::object::{kind, ObjectExt}; +use serde_derive::{Deserialize, Serialize}; + +pub mod properties; + +use self::{kind::*, properties::*}; + +/// The ActivityPub Object Extension Trait +/// +/// This trait provides generic access to an activitypub object's properties +pub trait ApObjectExt: Object { + fn props(&self) -> &ApObjectProperties; + fn props_mut(&mut self) -> &mut ApObjectProperties; +} + +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Article { + #[serde(rename = "type")] + kind: ArticleType, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, +} + +impl Object for Article {} +impl ObjectExt for Article { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Article { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} + +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Audio { + #[serde(rename = "type")] + kind: AudioType, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, +} + +impl Object for Audio {} +impl ObjectExt for Audio { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Audio { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} + +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Document { + #[serde(rename = "type")] + kind: DocumentType, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, +} + +impl Object for Document {} +impl ObjectExt for Document { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Document { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} + +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Event { + #[serde(rename = "type")] + kind: EventType, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, +} + +impl Object for Event {} +impl ObjectExt for Event { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Event { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} + +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Image { + #[serde(rename = "type")] + kind: ImageType, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, +} + +impl Object for Image {} +impl ObjectExt for Image { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Image { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} + +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Note { + #[serde(rename = "type")] + kind: NoteType, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, +} + +impl Object for Note {} +impl ObjectExt for Note { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Note { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} + +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Page { + #[serde(rename = "type")] + kind: PageType, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, +} + +impl Object for Page {} +impl ObjectExt for Page { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Page { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} + +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Place { + #[serde(rename = "type")] + kind: PlaceType, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + #[serde(flatten)] + pub place_props: PlaceProperties, +} + +impl Object for Place {} +impl ObjectExt for Place { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Place { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} + +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Profile { + #[serde(rename = "type")] + kind: ProfileType, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + #[serde(flatten)] + pub profile_props: ProfileProperties, +} + +impl Object for Profile {} +impl ObjectExt for Profile { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Profile { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} + +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Relationship { + #[serde(rename = "type")] + kind: RelationshipType, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + #[serde(flatten)] + pub relationship_props: RelationshipProperties, +} + +impl Object for Relationship {} +impl ObjectExt for Relationship { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Relationship { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} + +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Tombstone { + #[serde(rename = "type")] + kind: TombstoneType, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, + + #[serde(flatten)] + pub tombstone_props: TombstoneProperties, +} + +impl Object for Tombstone {} +impl ObjectExt for Tombstone { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Tombstone { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} + +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Video { + #[serde(rename = "type")] + kind: VideoType, + + #[serde(flatten)] + pub object_props: ObjectProperties, + + #[serde(flatten)] + pub ap_object_props: ApObjectProperties, +} + +impl Object for Video {} +impl ObjectExt for Video { + fn props(&self) -> &ObjectProperties { + &self.object_props + } + + fn props_mut(&mut self) -> &mut ObjectProperties { + &mut self.object_props + } +} +impl ApObjectExt for Video { + fn props(&self) -> &ApObjectProperties { + &self.ap_object_props + } + + fn props_mut(&mut self) -> &mut ApObjectProperties { + &mut self.ap_object_props + } +} diff --git a/server/src/activitypub/object/properties.rs b/server/src/activitypub/object/properties.rs new file mode 100644 index 000000000..ea4f004f4 --- /dev/null +++ b/server/src/activitypub/object/properties.rs @@ -0,0 +1,111 @@ +/* + * This file is part of ActivityPub. + * + * Copyright © 2018 Riley Trautman + * + * ActivityPub is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ActivityPub is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ActivityPub. If not, see . + */ + +//! Namespace for properties of standard Object types +//! +//! To use these properties in your own types, you can flatten them into your struct with serde: +//! +//! ```rust +//! use activitypub::{Object, object::properties::ApObjectProperties}; +//! use serde_derive::{Deserialize, Serialize}; +//! +//! #[derive(Clone, Debug, Serialize, Deserialize)] +//! #[serde(rename_all = "camelCase")] +//! pub struct MyObject { +//! #[serde(rename = "type")] +//! pub kind: String, +//! +//! /// Define a require property for the MyObject type +//! pub my_property: String, +//! +//! #[serde(flatten)] +//! pub object_props: ApObjectProperties, +//! } +//! +//! impl Object for MyObject {} +//! # +//! # fn main() {} +//! ``` + +use super::Object; + +use activitystreams_derive::Properties; +pub use activitystreams_types::object::properties::{ + ObjectProperties, PlaceProperties, ProfileProperties, RelationshipProperties, TombstoneProperties, +}; +use serde_derive::{Deserialize, Serialize}; + +/// Define activitypub properties for the Object type as described by the Activity Pub vocabulary. +#[derive(Clone, Debug, Default, Deserialize, Properties, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ApObjectProperties { + // TODO: IRI + /// This is a list of all Announce activities with this object as the object property, added as + /// a side effect. + /// + /// The shares collection MUST be either an OrderedCollection or a Collection and MAY be + /// filtered on privileges of an authenticated user or as appropriate when no authentication is + /// given. + /// + /// - Range: `anyUri` + /// - Functional: true + #[serde(skip_serializing_if = "Option::is_none")] + #[activitystreams(concrete(String), functional)] + pub shares: Option, + + /// This is a list of all Like activities with this object as the object property, added as a + /// side effect. + /// + /// The likes collection MUST be either an OrderedCollection or a Collection and MAY be + /// filtered on privileges of an authenticated user or as appropriate when no authentication is + /// given. + /// + /// - Range: `anyUri` + /// - Functional: true + #[serde(skip_serializing_if = "Option::is_none")] + #[activitystreams(concrete(String), functional)] + pub likes: Option, + + /// The source property is intended to convey some sort of source from which the content markup + /// was derived, as a form of provenance, or to support future editing by clients. + /// + /// In general, clients do the conversion from source to content, not the other way around. + /// + /// The value of source is itself an object which uses its own content and mediaType fields to + /// supply source information. + /// + /// - Range: `Object` + /// - Functional: true + #[serde(skip_serializing_if = "Option::is_none")] + #[activitystreams(ab(Object), concrete(String), functional)] + pub source: Option, + + /// Servers MAY support uploading document types to be referenced in activites, such as images, + /// video or other binary data, but the precise mechanism is out of scope for this version of + /// `ActivityPub`. + /// + /// The Social Web Community Group is refining the protocol in the + /// [`ActivityPub` Media Upload report](https://www.w3.org/wiki/SocialCG/ActivityPub/MediaUpload). + /// + /// - Range: `anyUri` + /// - Functional: false + #[serde(skip_serializing_if = "Option::is_none")] + #[activitystreams(concrete(String))] + pub upload_media: Option, +} diff --git a/server/src/apub/community.rs b/server/src/apub/community.rs index 621d41021..bc07b3190 100644 --- a/server/src/apub/community.rs +++ b/server/src/apub/community.rs @@ -1,9 +1,9 @@ +use crate::activitypub::{actor::Group, collection::UnorderedCollection, context}; use crate::apub::make_apub_endpoint; use crate::db::community::Community; use crate::db::community_view::CommunityFollowerView; use crate::db::establish_unpooled_connection; use crate::to_datetime_utc; -use activitypub::{actor::Group, collection::UnorderedCollection, context}; use actix_web::body::Body; use actix_web::web::Path; use actix_web::HttpResponse; @@ -36,8 +36,7 @@ impl Community { } if let Some(description) = &self.description { - group - .object_props.summary = Some(json!(description.to_string())); + group.object_props.summary = Some(json!(description.to_string())); } group diff --git a/server/src/apub/post.rs b/server/src/apub/post.rs index ebb171290..50b87c873 100644 --- a/server/src/apub/post.rs +++ b/server/src/apub/post.rs @@ -1,7 +1,7 @@ +use crate::activitypub::{context, object::Page}; use crate::apub::make_apub_endpoint; use crate::db::post::Post; use crate::to_datetime_utc; -use activitypub::{context, object::Page}; impl Post { pub fn as_page(&self) -> Page { diff --git a/server/src/apub/puller.rs b/server/src/apub/puller.rs index b6647060e..b3177183c 100644 --- a/server/src/apub/puller.rs +++ b/server/src/apub/puller.rs @@ -1,12 +1,12 @@ extern crate reqwest; use self::reqwest::Error; +use crate::activitypub::actor::Group; use crate::api::community::{GetCommunityResponse, ListCommunitiesResponse}; use crate::api::post::GetPosts; use crate::db::community_view::CommunityView; use crate::naive_now; use crate::settings::Settings; -use activitypub::actor::Group; // TODO: right now all of the data is requested on demand, for production we will need to store // things in the local database to not ruin the performance @@ -50,14 +50,34 @@ pub fn get_remote_community(identifier: String) -> Result().unwrap(), - name: name, - title: community.object_props.name.unwrap().as_str().unwrap().to_string(), // TODO: why does it still show !main@lemmy_beta:8541 + id: community + .object_props + .id + .unwrap() + .as_str() + .unwrap() + .parse::() + .unwrap(), + name, + title: community + .object_props + .name + .unwrap() + .as_str() + .unwrap() + .to_string(), // TODO: why does it still show !main@lemmy_beta:8541 description: community.object_props.summary.map(|c| c.to_string()), // TODO: this has an extra quote somehow category_id: -1, - creator_id: community.object_props.attributed_to.unwrap().as_str().unwrap().parse::().unwrap(), + creator_id: community + .object_props + .attributed_to + .unwrap() + .as_str() + .unwrap() + .parse::() + .unwrap(), removed: false, - published: naive_now(), // TODO: need to handle time conversion (or handle it in apub lib) + published: naive_now(), // TODO: need to handle time conversion (or handle it in apub lib) updated: Some(naive_now()), // TODO: community.object_props.updated deleted: false, nsfw: false, diff --git a/server/src/apub/user.rs b/server/src/apub/user.rs index 5f2421f11..fb31e4b68 100644 --- a/server/src/apub/user.rs +++ b/server/src/apub/user.rs @@ -1,8 +1,8 @@ +use crate::activitypub::{actor::Person, context}; use crate::apub::make_apub_endpoint; use crate::db::establish_unpooled_connection; use crate::db::user::User_; use crate::to_datetime_utc; -use activitypub::{actor::Person, context}; use actix_web::body::Body; use actix_web::web::Path; use actix_web::HttpResponse; diff --git a/server/src/lib.rs b/server/src/lib.rs index 3e22585de..5b37a08e6 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -22,6 +22,7 @@ pub extern crate serde_json; pub extern crate sha2; pub extern crate strum; +pub mod activitypub; pub mod api; pub mod apub; pub mod db; From 1f29e9179607761e8830b3f712c0245ed4fd4b9b Mon Sep 17 00:00:00 2001 From: Felix Date: Sat, 29 Feb 2020 18:38:47 +0100 Subject: [PATCH 045/164] Various minor federation improvements --- docker/federation-test/Dockerfile | 11 ++++---- server/src/apub/community.rs | 10 +++---- server/src/apub/mod.rs | 7 ++++- server/src/apub/puller.rs | 44 +++++++++++++------------------ 4 files changed, 33 insertions(+), 39 deletions(-) diff --git a/docker/federation-test/Dockerfile b/docker/federation-test/Dockerfile index 09f3681c0..d8302ea7a 100644 --- a/docker/federation-test/Dockerfile +++ b/docker/federation-test/Dockerfile @@ -1,14 +1,15 @@ FROM ekidd/rust-musl-builder:1.38.0-openssl11 +USER root +RUN mkdir /app/dist/documentation/ -p \ + && addgroup --gid 1001 lemmy \ + && adduser --disabled-password --shell /bin/sh -u 1001 --ingroup lemmy lemmy + # Copy resources COPY server/config/defaults.hjson /app/config/defaults.hjson -COPY server/target/debug/lemmy_server /app/lemmy COPY ui/dist /app/dist +COPY server/target/debug/lemmy_server /app/lemmy -USER root -RUN mkdir /app/dist/documentation/ -RUN addgroup --gid 1001 lemmy -RUN adduser --disabled-password --shell /bin/sh -u 1001 --ingroup lemmy lemmy RUN chown lemmy:lemmy /app/ -R USER lemmy EXPOSE 8536 diff --git a/server/src/apub/community.rs b/server/src/apub/community.rs index bc07b3190..79eb68398 100644 --- a/server/src/apub/community.rs +++ b/server/src/apub/community.rs @@ -18,6 +18,7 @@ impl Community { // TODO: why the hell is this code so awkward? group.object_props.set_context_object(context()).ok(); + // TODO: id really needs to be a url group.object_props.set_id_string(self.id.to_string()).ok(); group .object_props @@ -64,17 +65,12 @@ impl Community { let connection = establish_unpooled_connection(); //As we are an object, we validated that the community id was valid + // TODO: add a method that only returns count for better performance let community_followers = CommunityFollowerView::for_community(&connection, self.id).unwrap(); - // TODO: we definitely dont want to make our follower list public, we should only expose the count - let ap_followers = community_followers - .iter() - .map(|follower| make_apub_endpoint("u", &follower.user_name)) - .collect(); - collection .collection_props - .set_items_string_vec(ap_followers) + .set_total_items_u64(community_followers.len() as u64) .unwrap(); collection } diff --git a/server/src/apub/mod.rs b/server/src/apub/mod.rs index 7a8b74f28..9bac64a6e 100644 --- a/server/src/apub/mod.rs +++ b/server/src/apub/mod.rs @@ -96,9 +96,14 @@ mod tests { pub fn make_apub_endpoint(point: S, value: T) -> String { format!( - "https://{}/federation/{}/{}", + "{}://{}/federation/{}/{}", + get_apub_protocol_string(), Settings::get().hostname, point, value ) } + +pub fn get_apub_protocol_string() -> &'static str { + "http" +} diff --git a/server/src/apub/puller.rs b/server/src/apub/puller.rs index b3177183c..3ab4c69bd 100644 --- a/server/src/apub/puller.rs +++ b/server/src/apub/puller.rs @@ -7,6 +7,7 @@ use crate::api::post::GetPosts; use crate::db::community_view::CommunityView; use crate::naive_now; use crate::settings::Settings; +use serde_json::Value; // TODO: right now all of the data is requested on demand, for production we will need to store // things in the local database to not ruin the performance @@ -32,7 +33,7 @@ pub fn get_remote_community_posts(name: String) -> Result { unimplemented!() } -pub fn get_remote_community(identifier: String) -> Result { +pub fn get_remote_community(identifier: String) -> Result { let x: Vec<&str> = identifier.split('@').collect(); let name = x[0].replace("!", ""); let instance = x[1]; @@ -48,34 +49,13 @@ pub fn get_remote_community(identifier: String) -> Result() - .unwrap(), + id: get_string_value(community.object_props.id).parse::()?, name, - title: community - .object_props - .name - .unwrap() - .as_str() - .unwrap() - .to_string(), // TODO: why does it still show !main@lemmy_beta:8541 - description: community.object_props.summary.map(|c| c.to_string()), // TODO: this has an extra quote somehow + title: get_string_value(community.object_props.name), + description: get_string_value_opt(community.object_props.summary), category_id: -1, - creator_id: community - .object_props - .attributed_to - .unwrap() - .as_str() - .unwrap() - .parse::() - .unwrap(), + creator_id: get_string_value(community.object_props.attributed_to).parse::()?, removed: false, published: naive_now(), // TODO: need to handle time conversion (or handle it in apub lib) updated: Some(naive_now()), // TODO: community.object_props.updated @@ -95,6 +75,18 @@ pub fn get_remote_community(identifier: String) -> Result) -> Option { + value + .as_ref() + .map(Value::as_str) + .flatten() + .map(str::to_string) +} + +fn get_string_value(value: Option) -> String { + get_string_value_opt(value).unwrap() +} + pub fn get_following_instances() -> Result, Error> { let instance_list = match Settings::get().federated_instance.clone() { Some(f) => vec![f, Settings::get().hostname.clone()], From 434bf35a55d597d7c901f0a0f4f979598ec67831 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Sat, 29 Feb 2020 13:03:41 -0500 Subject: [PATCH 046/164] Refactoring thumbnails. Fixes #564 - Adding a default discussion thumbnail - Adding a cropping max-height, and consistent width. - Getting rid of hover overlays, in favor of top right content-type icon. --- ui/assets/css/main.css | 19 +- ui/src/components/post-listing.tsx | 1008 +++++++++++++++------------- ui/src/components/symbols.tsx | 4 + 3 files changed, 558 insertions(+), 473 deletions(-) diff --git a/ui/assets/css/main.css b/ui/assets/css/main.css index 048f687ec..fd51c5948 100644 --- a/ui/assets/css/main.css +++ b/ui/assets/css/main.css @@ -131,8 +131,13 @@ blockquote { } .thumbnail { - max-height: 62px; - max-width: 400px; + object-fit: cover; + max-height: 80px; + width: 100%; +} + +svg.thumbnail { + height: 40px; } .no-s-hows { @@ -188,6 +193,16 @@ hr { border: unset; } +.mini-overlay { + position: absolute; + top: 0; + right: 0; + padding: 2px; + background: rgba(0,0,0,.4); + border-bottom-left-radius: 0.25rem !important; + border-top-right-radius: 0.25rem !important; +} + .link-overlay:hover { transition: .1s; opacity: 1; diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index a77021584..90a8b7a11 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -121,9 +121,12 @@ export class PostListing extends Component { render() { return ( -
+
{!this.state.showEdit ? ( - this.listing() + <> + {this.listing()} + {this.body()} + ) : (
{ ); } - imgThumbnail() { + body() { + return ( +
+
+ {this.state.url && this.props.showBody && this.state.iframely && ( + + )} + {this.props.showBody && this.props.post.body && ( + <> + {this.state.viewSource ? ( +
{this.props.post.body}
+ ) : ( +
+ )} + + )} +
+
+ ); + } + + imgThumb() { let post = this.props.post; return ( - + src={imageThumbnailer(this.state.thumbnail)} + /> ); } + thumbnail() { + let post = this.props.post; + + if (isImage(this.state.url)) { + return ( + + {this.imgThumb()} + + + + + ); + } else if (this.state.thumbnail) { + return ( + + {this.imgThumb()} + + + + + ); + } else if (this.state.url && !this.state.thumbnail) { + return ( + + + + + + ); + } else { + return ( + + + + + + ); + } + } + listing() { let post = this.props.post; return ( -
-
+
+
)}
- {this.state.thumbnail && !this.state.imageExpanded && ( -
- {isImage(this.state.url) ? ( - - {this.imgThumbnail()} - - - - - ) : ( - - {this.imgThumbnail()} - - - - - )} + {!this.state.imageExpanded && ( +
+
{this.thumbnail()}
)} {this.state.url && isVideo(this.state.url) && ( @@ -212,501 +274,505 @@ export class PostListing extends Component { muted loop controls - class="mx-2 mt-1 float-left" + class="col-2 pr-0 mt-1" height="100" width="150" > )} -
-
-
- {this.props.showBody && this.state.url ? ( - - {post.name} - - ) : ( - - {post.name} - - )} -
- {this.state.url && - !( - new URL(this.state.url).hostname == window.location.hostname - ) && ( - - - {new URL(this.state.url).hostname} - - - - - - )} - {this.state.thumbnail && ( - <> - {!this.state.imageExpanded ? ( - - [+] - - ) : ( - - +
+
+
+
+ {this.props.showBody && this.state.url ? ( + - [-] - -
+ {post.name} + + ) : ( + + {post.name} + + )} +
+ {this.state.url && + !( + new URL(this.state.url).hostname == window.location.hostname + ) && ( + + + {new URL(this.state.url).hostname} + + + + + + )} + {this.state.thumbnail && ( + <> + {!this.state.imageExpanded ? ( - - - - - + [+] -
- + ) : ( + + + [-] + +
+ + + +
+
+ )} + )} - - )} - {post.removed && ( - - {i18n.t('removed')} - - )} - {post.deleted && ( - - {i18n.t('deleted')} - - )} - {post.locked && ( - - {i18n.t('locked')} - - )} - {post.stickied && ( - - {i18n.t('stickied')} - - )} - {post.nsfw && ( - - {i18n.t('nsfw')} - - )} + {post.removed && ( + + {i18n.t('removed')} + + )} + {post.deleted && ( + + {i18n.t('deleted')} + + )} + {post.locked && ( + + {i18n.t('locked')} + + )} + {post.stickied && ( + + {i18n.t('stickied')} + + )} + {post.nsfw && ( + + {i18n.t('nsfw')} + + )} +
+
-
-
-
    -
  • - {i18n.t('by')} - - {post.creator_avatar && showAvatars() && ( - - )} - {post.creator_name} - - {this.isMod && ( - {i18n.t('mod')} - )} - {this.isAdmin && ( - - {i18n.t('admin')} - - )} - {(post.banned_from_community || post.banned) && ( - - {i18n.t('banned')} - - )} - {this.props.showCommunity && ( - - {i18n.t('to')} - - {post.community_name} +
    +
    +
      +
    • + {i18n.t('by')} + + {post.creator_avatar && showAvatars() && ( + + )} + {post.creator_name} - - )} -
    • -
    • - - - -
    • -
    • - - (+{this.state.upvotes} - | - -{this.state.downvotes} - ) - -
    • -
    • - - {i18n.t('number_of_comments', { - count: post.number_of_comments, - })} - -
    • -
    -
      - {this.props.post.duplicates && ( - <> -
    • - {i18n.t('cross_posted_to')} -
    • - {this.props.post.duplicates.map(post => ( -
    • - {post.community_name} -
    • - ))} - - )} -
    -
      - {UserService.Instance.user && ( - <> - {this.props.showBody && ( - <> -
    • - - {post.saved ? i18n.t('unsave') : i18n.t('save')} - -
    • -
    • - - {i18n.t('cross_post')} + {this.isMod && ( + + {i18n.t('mod')} + + )} + {this.isAdmin && ( + + {i18n.t('admin')} + + )} + {(post.banned_from_community || post.banned) && ( + + {i18n.t('banned')} + + )} + {this.props.showCommunity && ( + + {i18n.t('to')} + + {post.community_name} -
    • - - )} - {this.myPost && this.props.showBody && ( + + )} + +
    • + + + +
    • +
    • + + (+{this.state.upvotes} + | + -{this.state.downvotes} + ) + +
    • +
    • + + {i18n.t('number_of_comments', { + count: post.number_of_comments, + })} + +
    • +
    +
      + {this.props.post.duplicates && ( <> -
    • - - {i18n.t('edit')} - -
    • - - {!post.deleted ? i18n.t('delete') : i18n.t('restore')} - + {i18n.t('cross_posted_to')}
    • + {this.props.post.duplicates.map(post => ( +
    • + + {post.community_name} + +
    • + ))} )} - {this.canModOnSelf && ( +
    +
      + {UserService.Instance.user && ( <> -
    • - - {post.locked ? i18n.t('unlock') : i18n.t('lock')} - -
    • -
    • - - {post.stickied ? i18n.t('unsticky') : i18n.t('sticky')} - -
    • - - )} - {/* Mods can ban from community, and appoint as mods to community */} - {(this.canMod || this.canAdmin) && ( -
    • - {!post.removed ? ( - - {i18n.t('remove')} - - ) : ( - - {i18n.t('restore')} - - )} -
    • - )} - {this.canMod && ( - <> - {!this.isMod && ( -
    • - {!post.banned_from_community ? ( + {this.props.showBody && ( + <> +
    • - {i18n.t('ban')} + {post.saved ? i18n.t('unsave') : i18n.t('save')} + +
    • +
    • + + {i18n.t('cross_post')} + +
    • + + )} + {this.myPost && this.props.showBody && ( + <> +
    • + + {i18n.t('edit')} + +
    • +
    • + + {!post.deleted + ? i18n.t('delete') + : i18n.t('restore')} + +
    • + + )} + {this.canModOnSelf && ( + <> +
    • + + {post.locked ? i18n.t('unlock') : i18n.t('lock')} + +
    • +
    • + + {post.stickied + ? i18n.t('unsticky') + : i18n.t('sticky')} + +
    • + + )} + {/* Mods can ban from community, and appoint as mods to community */} + {(this.canMod || this.canAdmin) && ( +
    • + {!post.removed ? ( + + {i18n.t('remove')} ) : ( - {i18n.t('unban')} + {i18n.t('restore')} )}
    • )} - {!post.banned_from_community && ( -
    • - - {this.isMod - ? i18n.t('remove_as_mod') - : i18n.t('appoint_as_mod')} - -
    • - )} - - )} - {/* Community creators and admins can transfer community to another mod */} - {(this.amCommunityCreator || this.canAdmin) && this.isMod && ( -
    • - {!this.state.showConfirmTransferCommunity ? ( - - {i18n.t('transfer_community')} - - ) : ( + {this.canMod && ( <> - - {i18n.t('are_you_sure')} - - - {i18n.t('yes')} - - - {i18n.t('no')} - + {!this.isMod && ( +
    • + {!post.banned_from_community ? ( + + {i18n.t('ban')} + + ) : ( + + {i18n.t('unban')} + + )} +
    • + )} + {!post.banned_from_community && ( +
    • + + {this.isMod + ? i18n.t('remove_as_mod') + : i18n.t('appoint_as_mod')} + +
    • + )} )} - - )} - {/* Admins can ban from all, and appoint other admins */} - {this.canAdmin && ( - <> - {!this.isAdmin && ( + {/* Community creators and admins can transfer community to another mod */} + {(this.amCommunityCreator || this.canAdmin) && this.isMod && (
    • - {!post.banned ? ( + {!this.state.showConfirmTransferCommunity ? ( - {i18n.t('ban_from_site')} + {i18n.t('transfer_community')} ) : ( - - {i18n.t('unban_from_site')} - + <> + + {i18n.t('are_you_sure')} + + + {i18n.t('yes')} + + + {i18n.t('no')} + + )}
    • )} - {!post.banned && ( + {/* Admins can ban from all, and appoint other admins */} + {this.canAdmin && ( + <> + {!this.isAdmin && ( +
    • + {!post.banned ? ( + + {i18n.t('ban_from_site')} + + ) : ( + + {i18n.t('unban_from_site')} + + )} +
    • + )} + {!post.banned && ( +
    • + + {this.isAdmin + ? i18n.t('remove_as_admin') + : i18n.t('appoint_as_admin')} + +
    • + )} + + )} + {/* Site Creator can transfer to another admin */} + {this.amSiteCreator && this.isAdmin && (
    • - - {this.isAdmin - ? i18n.t('remove_as_admin') - : i18n.t('appoint_as_admin')} - + {!this.state.showConfirmTransferSite ? ( + + {i18n.t('transfer_site')} + + ) : ( + <> + + {i18n.t('are_you_sure')} + + + {i18n.t('yes')} + + + {i18n.t('no')} + + + )}
    • )} )} - {/* Site Creator can transfer to another admin */} - {this.amSiteCreator && this.isAdmin && ( + {this.props.showBody && post.body && (
    • - {!this.state.showConfirmTransferSite ? ( - - {i18n.t('transfer_site')} - - ) : ( - <> - - {i18n.t('are_you_sure')} - - - {i18n.t('yes')} - - - {i18n.t('no')} - - - )} + + {i18n.t('view_source')} +
    • )} - - )} - {this.props.showBody && post.body && ( -
    • - + {this.state.showRemoveDialog && ( + - {i18n.t('view_source')} - -
    • - )} -
    - {this.state.url && this.props.showBody && this.state.iframely && ( - - )} - {this.state.showRemoveDialog && ( - - - - - )} - {this.state.showBanDialog && ( -
    -
    - - -
    - {/* TODO hold off on expires until later */} - {/*
    */} - {/* */} - {/* */} - {/*
    */} -
    - -
    -
    - )} - {this.props.showBody && post.body && ( - <> - {this.state.viewSource ? ( -
    {post.body}
    - ) : ( -
    + + + )} - - )} + {this.state.showBanDialog && ( +
    +
    + + +
    + {/* TODO hold off on expires until later */} + {/*
    */} + {/* */} + {/* */} + {/*
    */} +
    + +
    +
    + )} +
    +
); diff --git a/ui/src/components/symbols.tsx b/ui/src/components/symbols.tsx index 559a97072..db1f7b5f5 100644 --- a/ui/src/components/symbols.tsx +++ b/ui/src/components/symbols.tsx @@ -15,6 +15,10 @@ export class Symbols extends Component { xmlnsXlink="http://www.w3.org/1999/xlink" > + + bubble2 + + image From c0802c7fa27602d183d82621e9412a8bf1931c76 Mon Sep 17 00:00:00 2001 From: Felix Date: Fri, 28 Feb 2020 18:48:30 +0100 Subject: [PATCH 047/164] make i18n compatible with weblate --- README.md | 2 +- ui/assets/translations/ca.json | 237 +++++++++++++++++++++++++++++ ui/assets/translations/de.json | 208 +++++++++++++++++++++++++ ui/assets/translations/en.json | 238 +++++++++++++++++++++++++++++ ui/assets/translations/eo.json | 175 +++++++++++++++++++++ ui/assets/translations/es.json | 240 +++++++++++++++++++++++++++++ ui/assets/translations/fa.json | 167 +++++++++++++++++++++ ui/assets/translations/fi.json | 235 +++++++++++++++++++++++++++++ ui/assets/translations/fr.json | 240 +++++++++++++++++++++++++++++ ui/assets/translations/it.json | 190 +++++++++++++++++++++++ ui/assets/translations/nl.json | 232 ++++++++++++++++++++++++++++ ui/assets/translations/pt_br.json | 239 +++++++++++++++++++++++++++++ ui/assets/translations/ru.json | 168 +++++++++++++++++++++ ui/assets/translations/sv.json | 193 ++++++++++++++++++++++++ ui/assets/translations/zh.json | 162 ++++++++++++++++++++ ui/package.json | 1 + ui/src/i18next.ts | 53 ++----- ui/src/translations/ca.ts | 239 ----------------------------- ui/src/translations/de.ts | 210 -------------------------- ui/src/translations/en.ts | 240 ----------------------------- ui/src/translations/eo.ts | 177 ---------------------- ui/src/translations/es.ts | 242 ------------------------------ ui/src/translations/fa.ts | 169 --------------------- ui/src/translations/fi.ts | 236 ----------------------------- ui/src/translations/fr.ts | 242 ------------------------------ ui/src/translations/it.ts | 192 ------------------------ ui/src/translations/nl.ts | 234 ----------------------------- ui/src/translations/pt_br.ts | 241 ----------------------------- ui/src/translations/ru.ts | 170 --------------------- ui/src/translations/sv.ts | 195 ------------------------ ui/src/translations/zh.ts | 164 -------------------- 31 files changed, 2939 insertions(+), 2992 deletions(-) create mode 100644 ui/assets/translations/ca.json create mode 100644 ui/assets/translations/de.json create mode 100644 ui/assets/translations/en.json create mode 100644 ui/assets/translations/eo.json create mode 100644 ui/assets/translations/es.json create mode 100644 ui/assets/translations/fa.json create mode 100644 ui/assets/translations/fi.json create mode 100644 ui/assets/translations/fr.json create mode 100644 ui/assets/translations/it.json create mode 100644 ui/assets/translations/nl.json create mode 100644 ui/assets/translations/pt_br.json create mode 100644 ui/assets/translations/ru.json create mode 100644 ui/assets/translations/sv.json create mode 100644 ui/assets/translations/zh.json delete mode 100644 ui/src/translations/ca.ts delete mode 100644 ui/src/translations/de.ts delete mode 100644 ui/src/translations/en.ts delete mode 100644 ui/src/translations/eo.ts delete mode 100644 ui/src/translations/es.ts delete mode 100644 ui/src/translations/fa.ts delete mode 100644 ui/src/translations/fi.ts delete mode 100644 ui/src/translations/fr.ts delete mode 100644 ui/src/translations/it.ts delete mode 100644 ui/src/translations/nl.ts delete mode 100644 ui/src/translations/pt_br.ts delete mode 100644 ui/src/translations/ru.ts delete mode 100644 ui/src/translations/sv.ts delete mode 100644 ui/src/translations/zh.ts diff --git a/README.md b/README.md index 81eceb74a..ada1ad5bb 100644 --- a/README.md +++ b/README.md @@ -124,7 +124,7 @@ Lemmy is free, open-source software, meaning no advertising, monetizing, or vent ### Translations -If you'd like to add translations, take 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/assets/translations/en.json). - Languages supported: Brazilian Portuguese (`pt-br`), 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`). diff --git a/ui/assets/translations/ca.json b/ui/assets/translations/ca.json new file mode 100644 index 000000000..e238ffa72 --- /dev/null +++ b/ui/assets/translations/ca.json @@ -0,0 +1,237 @@ +{ + "post": "Publicar", + "remove_post": "Eliminar publicació", + "no_posts": "Sense publicacions.", + "create_a_post": "Crear una publicació", + "create_post": "Crear Publicació", + "number_of_posts": "{{count}} Publicacions", + "posts": "Publicacions", + "related_posts": "Aquestes publicacions podrien estar relacionades", + "cross_posts": "Aquest link també ha sigut publicat en:", + "cross_post": "cross-post", + "comments": "Comentaris", + "number_of_comments": "{{count}} Comentaris", + "remove_comment": "Eliminar Comentaris", + "communities": "Comunitats", + "users": "Usuaris", + "create_a_community": "Crear una comunitat", + "create_community": "Crear Comunitat", + "remove_community": "Eliminar Comunitat", + "subscribed_to_communities": "Subscrit a <1>comunitats", + "trending_communities": "<1>Comunitats en tendència", + "list_of_communities": "Llista de comunitats", + "number_of_communities": "{{count}} Comunitats", + "community_reqs": "minúscules, guió baix, i sense espais.", + "create_private_message": "Crear Missatge Privat", + "send_secure_message": "Enviar Missatge Segur", + "send_message": "Enviar Missatge", + "message": "Missatge", + "edit": "editar", + "reply": "respondre", + "cancel": "Cancelar", + "preview": "Previsualitzar", + "upload_image": "pujar imatge", + "avatar": "Avatar", + "upload_avatar": "Pujar Avatar", + "show_avatars": "Veure Avatares", + "formatting_help": "Ajuda de format", + "view_source": "veure font", + "unlock": "desbloquejar", + "lock": "bloquejar", + "sticky": "fijat", + "unsticky": "no fijat", + "link": "link", + "archive_link": "arxivar link", + "mod": "moderador", + "mods": "moderadores", + "moderates": "Modera", + "settings": "Configuració", + "remove_as_mod": "eliminar com moderador", + "appoint_as_mod": "designar com moderador", + "modlog": "Historial de moderació", + "admin": "administrador", + "admins": "administradors", + "remove_as_admin": "eliminar com administrador", + "appoint_as_admin": "designar com administrador", + "remove": "eliminar", + "removed": "eliminat", + "locked": "bloquejat", + "stickied": "fijat", + "reason": "Raó", + "mark_as_read": "marcar com llegit", + "mark_as_unread": "marcar com no llegit", + "delete": "eliminar", + "deleted": "eliminat", + "delete_account": "Eliminar Compte", + "delete_account_confirm": + "Avís: aquesta acció eliminarà permanentment la teva informació. Introdueix la teva contrasenya per a continuar", + "restore": "restaurar", + "ban": "expulsar", + "ban_from_site": "expulsar del lloc", + "unban": "admetre", + "unban_from_site": "admetre al lloc", + "banned": "expulsat", + "save": "guardar", + "unsave": "descartar", + "create": "crear", + "creator": "creador", + "username": "Nom d'Usuari", + "email_or_username": "Correu o Usuari", + "number_of_users": "{{count}} Usuaris", + "number_of_subscribers": "{{count}} Subscriptors", + "number_of_points": "{{count}} Punts", + "number_online": "{{count}} Usauris En Línia", + "name": "Nom", + "title": "Titol", + "category": "Categoria", + "subscribers": "Suscriptors", + "both": "Ambdos", + "saved": "Guardat", + "unsubscribe": "Desubscriure's", + "subscribe": "Subscriure's", + "subscribed": "Subscrit", + "prev": "Anterior", + "next": "Següent", + "sidebar": "Descripció de la comunitat", + "sort_type": "Tipus d'orden", + "hot": "Popular", + "new": "Nou", + "top_day": "El millor del dia", + "week": "Setmana", + "month": "Mes", + "year": "Any", + "all": "Tot", + "top": "Millor", + "api": "API", + "docs": "Docs", + "inbox": "Bústia d'entrada", + "inbox_for": "Bústia d'entrada per a <1>{{user}}", + "mark_all_as_read": "marcar tot com llegit", + "type": "Tipus", + "unread": "No llegit", + "replies": "Respostes", + "mentions": "Menciones", + "reply_sent": "Resposta enviada", + "message_sent": "Missatge enviado", + "search": "Buscar", + "overview": "Resum", + "view": "Vista", + "logout": "Tancar sessió", + "login_sign_up": "Iniciar sessió / Crear compte", + "login": "Iniciar sessió", + "sign_up": "Crear compte", + "notifications_error": + "Notificacions d'escriptori no disponibles al teu navegador. Prova amb Firefox o Chrome.", + "unread_messages": "Missatges no llegits", + "messages": "Missatges", + "password": "Contrasenya", + "verify_password": "Verificar Contrasenya", + "old_password": "Antiga Contrasenya", + "forgot_password": "oblidí la meva contrasenya", + "reset_password_mail_sent": "Enviar correu per a restablir la contrasenya.", + "password_change": "Canvi de Contrasenya", + "new_password": "Nueva Contrasenya", + "no_email_setup": "Aquest servidor no ha activat correctament el correu.", + "email": "Correu electrònic", + "matrix_user_id": "Usuari Matricial", + "private_message_disclaimer": + "Avís: Els missatges privats en Lemmy no són segurs. Sisplau creu un compte en <1>Riot.im per a mensajeria segura.", + "send_notifications_to_email": "Enviar notificacions al correu", + "optional": "Opcional", + "expires": "Expira", + "language": "Llenguatge", + "browser_default": "Per defecte del navegador", + "downvotes_disabled": "Vots negatius deshabilitats", + "enable_downvotes": "Habilitar vots negatius", + "open_registration": "Obrir registre", + "registration_closed": "Registre tancat", + "enable_nsfw": "Habilitar NSFW", + "url": "URL", + "body": "Descripció", + "copy_suggested_title": "Copiar el títol sugerido: {{title}}", + "community": "Comunitat", + "expand_here": "Expandir ací", + "subscribe_to_communities": "Subscriure's a algunes <1>comunitats.", + "chat": "Chat", + "recent_comments": "Comentaris recients", + "no_results": "Sense resultats.", + "setup": "Configurar", + "lemmy_instance_setup": "Configuració d'instancia de Lemmy", + "setup_admin": "Configurar administrador del Lloc", + "your_site": "el teu lloc", + "modified": "modificat", + "nsfw": "NSFW", + "show_nsfw": "Mostrar contingut NSFW", + "theme": "Tema", + "sponsors": "Patrocinadors", + "sponsors_of_lemmy": "Patrocinadors de Lemmy", + "sponsor_message": + "Lemmy és programari lliure i de <1>codi obert, la qual cosa significa que no tindrà publicitats, monetització, ni capitals emprenedors, mai. Les teves donacions secunden directament el desenvolupament a temps complet del projecte. Moltes gràcies a les següents persones:", + "support_on_patreon": "Suport a Patreon", + "donate_to_lemmy": "Donar a Lemmy", + "donate": "Donar", + "general_sponsors": + "Los Patrocinadores Generales son aquellos que señaron entre $10 y $39 a Lemmy.", + "crypto": "Crypto", + "bitcoin": "Bitcoin", + "ethereum": "Ethereum", + "monero": "Monero", + "code": "Codi", + "joined": "Es va unir", + "by": "per", + "to": "a", + "from": "des de", + "transfer_community": "transferir comunitat", + "transfer_site": "transferir lloc", + "are_you_sure": "Ets segur?", + "yes": "sí", + "no": "no", + "powered_by": "Impulsat per", + "landing_0": + "Lemmy és un <1>agregador de links / alternativa a reddit, amb la intenció de funcionar al <2>fedivers.<3>És allotjable per un mateix (sense necessitat de grans companyies), té actualització en directe de cadenes de comentaris, i és petit (<4>~80kB). Federar amb el sistema de xarxes ActivityPub forma part dels objectius del projecte. <5>Aquesta és una <6>versió beta molt prematura, i actualment moltes de les característiques són trencades o falten. <7>Suggereix noves característiques o reporta errors <8>aquí.<9>Fet amb <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.", + "not_logged_in": "No has iniciat sessió.", + "logged_in": "Has iniciat sessió.", + "community_ban": "Has sigut expulsat d'aquesta comunitat.", + "site_ban": "Has sigut expulsat d'aquest lloc.", + "couldnt_create_comment": "No s'ha pogut crear el comentari.", + "couldnt_like_comment": "No s'ha pogut donar m'agrada al comentari.", + "couldnt_update_comment": "No s'ha pogut actualitzar el comentari.", + "couldnt_save_comment": "No s'ha pogut guardar el comentari.", + "no_comment_edit_allowed": "No tens permisos per a editar el comentari.", + "no_post_edit_allowed": "No tens permisos per a editar la publicació.", + "no_community_edit_allowed": "No tens permisos per a editar la comunitat.", + "couldnt_find_community": "No s'ha pogut trobar la comunitat.", + "couldnt_update_community": "No s'ha pogut actualitzar la comunitat.", + "community_already_exists": "Aquesta comunitat ja existeix.", + "community_moderator_already_exists": + "Aquest moderador de la comunitat ja existeix.", + "community_follower_already_exists": + "Aquest seguidor de la comunitat ja existeix.", + "community_user_already_banned": + "Aquest usuari de la comunitat ja fou expulsat.", + "couldnt_create_post": "No s'ha pogut crear la publicació.", + "couldnt_like_post": "No s'ha pogut donar m'agrada a la publicació.", + "couldnt_find_post": "No s'ha pogut trobar la publicació.", + "couldnt_get_posts": "No s'han pogut obtindre les publicacions.", + "couldnt_update_post": "No s'ha pogut actualitzar la publicació.", + "couldnt_save_post": "No s'ha pogut guardar la publicació.", + "no_slurs": "Prohibit insultar.", + "not_an_admin": "No és un administrador.", + "site_already_exists": "El lloc ja existeix.", + "couldnt_update_site": "No s'ha pogut actualitzar el lloc.", + "couldnt_find_that_username_or_email": + "No s'ha pogut trobar aquest nom de usuari o correu electrònic.", + "password_incorrect": "Contrasenya incorrecta.", + "passwords_dont_match": "Les contrasenyes no coincideixen.", + "admin_already_created": "Ho sentim, ja hi ha un adminisitrador.", + "user_already_exists": "L'usuari ja existeix.", + "email_already_exists": "El correu ja és en ús.", + "couldnt_update_user": "No s'ha pogut actualitzar l'usuari.", + "system_err_login": + "Error del sistema. Intenti tancar sessió i ingressar de nou.", + "couldnt_create_private_message": "No s'ha pogut crear el missatge privat.", + "no_private_message_edit_allowed": + "Sense permisos per a editar el missatge privat.", + "couldnt_update_private_message": + "No s'ha pogut actualitzar el missatge privat." +} diff --git a/ui/assets/translations/de.json b/ui/assets/translations/de.json new file mode 100644 index 000000000..49b4b3df6 --- /dev/null +++ b/ui/assets/translations/de.json @@ -0,0 +1,208 @@ +{ + "post": "post", + "remove_post": "Beitrag löschen", + "no_posts": "Keine Beiträge.", + "create_a_post": "Einen Beitrag anlegen", + "create_post": "Beitrag anlegen", + "number_of_posts": "{{count}} Beiträge", + "posts": "Beiträge", + "related_posts": "Diese Beiträge könnten verwandt sein", + "cross_posts": "Dieser Link wurde auch veröffentlicht unter:", + "cross_post": "Crosspost", + "comments": "Kommentare", + "number_of_comments": "{{count}} Kommentare", + "remove_comment": "Kommentar löschen", + "communities": "Communities", + "users": "Benutzer", + "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", + "number_of_communities": "{{count}} Communities", + "community_reqs": "Kleinbuchstaben, Großbuchstaben und keine Leerzeichen.", + "edit": "editieren", + "reply": "antworten", + "cancel": "Abbrechen", + "preview": "Vorschau", + "upload_image": "Bild hochladen", + "formatting_help": "Formatierungshilfe", + "view_source": "Quelle anzeigen", + "unlock": "entsperren", + "lock": "sperren", + "sticky": "haftend", + "unsticky": "nicht haftend", + "link": "link", + "archive_link": "Archiv-Link", + "mod": "Moderator", + "mods": "Moderatoren", + "moderates": "Moderiert", + "settings": "Einstellungen", + "remove_as_mod": "Als Moderator entfernen", + "appoint_as_mod": "Zum Moderator ernennen", + "modlog": "Modlog", + "admin": "Administrator", + "admins": "Administratoren", + "remove_as_admin": "Als Administrator entfernen", + "appoint_as_admin": "Zum Administrator ernennen", + "remove": "entfernen", + "removed": "entfernt", + "locked": "gesperrt", + "stickied": "angeheftet", + "reason": "Grund", + "mark_as_read": "als gelesen markieren", + "mark_as_unread": "als ungelesen markieren", + "delete": "löschen", + "deleted": "gelöscht", + "delete_account": "Konto löschen", + "delete_account_confirm": + "Achtung: Dadurch werden alle Ihre Daten dauerhaft gelöscht. Geben Sie zur Bestätigung Ihr Passwort ein.", + "restore": "wiederherstellen", + "ban": "bannen", + "ban_from_site": "Von der Seite bannen", + "unban": "entbannen", + "unban_from_site": "Von der Seite entbannen", + "banned": "gesperrt", + "save": "speichern", + "unsave": "nicht speichern", + "create": "anlegen", + "creator": "Ersteller", + "username": "Benutzername", + "email_or_username": "E-mail oder Username", + "number_of_users": "{{count}} Benutzer", + "number_of_subscribers": "{{count}} Abonnenten", + "number_of_points": "{{count}} Punkte", + "number_online": "{{count}} Benutzer online", + "name": "Name", + "title": "Titel", + "category": "Kategorie", + "subscribers": "Abonnenten", + "both": "Beide", + "saved": "Gespeichert", + "unsubscribe": "Abbestellen", + "subscribe": "Abonnieren", + "subscribed": "Abonniert", + "prev": "Zurück", + "next": "Weiter", + "sidebar": "Seitenleiste", + "sort_type": "Sortieren nach", + "hot": "Hot", + "new": "Neu", + "top_day": "Top täglich", + "week": "Woche", + "month": "Monat", + "year": "Jahr", + "all": "Alle", + "top": "Top", + "api": "API", + "inbox": "Posteingang", + "inbox_for": "Posteingang für <1>{{user}}", + "mark_all_as_read": "Alle als gelesen markieren", + "type": "Typ", + "unread": "Ungelesen", + "replies": "Antworten", + "mentions": "Erwähnung", + "reply_sent": "Antwort gesendet", + "search": "Suchen", + "overview": "Übersicht", + "view": "Ansicht", + "logout": "Ausloggen", + "login_sign_up": "Einloggen / Registrieren", + "notifications_error": + "Desktop-Benachrichtigungen sind in deinem browser nicht verfügbar. Versuche Firefox oder Chrome.", + "unread_messages": "Ungelesene Nachrichten", + "password": "Passwort", + "verify_password": "Passwort überprüfen", + "forgot_password": "Passwort vergessen", + "reset_password_mail_sent": + "Eine E-Mail wurde geschickt, um dein Passwort zurückzusetzen.", + "password_change": "Passwort geändert", + "new_password": "neues Passwort", + "no_email_setup": "Dieser Server hat E-Mails nicht korrekt eingerichtet.", + "login": "Einloggen", + "sign_up": "Registrieren", + "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": "Gemeinschaft", + "expand_here": "hier erweitern", + "subscribe_to_communities": "Abonniere ein paar <1>communities.", + "chat": "Chat", + "recent_comments": "Neueste Kommentare", + "no_results": "Keine Ergebnisse.", + "setup": "Einrichten", + "lemmy_instance_setup": "Lemmy Instanz Einrichten", + "setup_admin": "Seiten Administrator konfigurieren", + "your_site": "deine Seite", + "modified": "verändert", + "nsfw": "NSFW", + "show_nsfw": "NSFW-Inhalte anzeigen", + "theme": "Aussehen", + "sponsors": "Sponsoren", + "sponsors_of_lemmy": "Sponsoren von Lemmy", + "sponsor_message": + "Lemmy ist freie <1>Open-Source Software, also ohne Werbung, Monetarisierung oder Venturekapital, Punkt. Deine Spenden gehen direkt an die Vollzeit Entwicklung des Projekts. Vielen Dank an die folgenden Personen:", + "support_on_patreon": "Auf Patreon unterstützen", + "support_on_liberapay": "Auf Liberapay unterstützen", + "general_sponsors": + "Allgemeine Sponsoren sind die, die zwischen $10 und $39 zu Lemmy beitragen.", + "crypto": "Kryptowährung", + "bitcoin": "Bitcoin", + "ethereum": "Ethereum", + "monero": "Monero", + "code": "Code", + "joined": "beigetreten", + "by": "von", + "to": "bis", + "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.", + "not_logged_in": "Nicht eingeloggt.", + "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.", + "couldnt_update_comment": "Konnte Kommentar nicht aktualisieren.", + "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 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": + "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.", + "couldnt_get_posts": "Konnte Beiträge nicht holen.", + "couldnt_update_post": "Konnte Beitrag nicht aktualisieren.", + "couldnt_save_post": "Konnte Beitrag nicht speichern.", + "no_slurs": "Keine Beleidigungen.", + "not_an_admin": "Kein Administrator.", + "site_already_exists": "Seite existiert bereits.", + "couldnt_update_site": "Konnte Seite nicht aktualisieren.", + "couldnt_find_that_username_or_email": + "Konnte Username oder E-Mail nicht finden.", + "password_incorrect": "Passwort falsch.", + "passwords_dont_match": "Passwörter stimmen nicht überein.", + "admin_already_created": "Entschuldigung, es gibt schon einen Administrator.", + "user_already_exists": "Nutzer existiert bereits.", + "couldnt_update_user": "Konnte Nutzer nicht aktualisieren", + "system_err_login": + "Systemfehler. Versuche dich aus- und wieder einzuloggen." +} diff --git a/ui/assets/translations/en.json b/ui/assets/translations/en.json new file mode 100644 index 000000000..54460022e --- /dev/null +++ b/ui/assets/translations/en.json @@ -0,0 +1,238 @@ +{ + "post": "post", + "remove_post": "Remove Post", + "no_posts": "No Posts.", + "create_a_post": "Create a post", + "create_post": "Create Post", + "number_of_posts": "{{count}} Posts", + "posts": "Posts", + "related_posts": "These posts might be related", + "cross_posts": "This link has also been posted to:", + "cross_post": "cross-post", + "cross_posted_to": "cross-posted to: ", + "comments": "Comments", + "number_of_comments": "{{count}} Comments", + "remove_comment": "Remove Comment", + "communities": "Communities", + "users": "Users", + "create_a_community": "Create a community", + "create_community": "Create Community", + "remove_community": "Remove Community", + "subscribed_to_communities": "Subscribed to <1>communities", + "trending_communities": "Trending <1>communities", + "list_of_communities": "List of communities", + "number_of_communities": "{{count}} Communities", + "community_reqs": "lowercase, underscores, and no spaces.", + "create_private_message": "Create Private Message", + "send_secure_message": "Send Secure Message", + "send_message": "Send Message", + "message": "Message", + "edit": "edit", + "reply": "reply", + "cancel": "Cancel", + "preview": "Preview", + "upload_image": "upload image", + "avatar": "Avatar", + "upload_avatar": "Upload Avatar", + "show_avatars": "Show Avatars", + "formatting_help": "formatting help", + "view_source": "view source", + "unlock": "unlock", + "lock": "lock", + "sticky": "sticky", + "unsticky": "unsticky", + "link": "link", + "archive_link": "archive link", + "mod": "mod", + "mods": "mods", + "moderates": "Moderates", + "settings": "Settings", + "remove_as_mod": "remove as mod", + "appoint_as_mod": "appoint as mod", + "modlog": "Modlog", + "admin": "admin", + "admins": "admins", + "remove_as_admin": "remove as admin", + "appoint_as_admin": "appoint as admin", + "remove": "remove", + "removed": "removed", + "locked": "locked", + "stickied": "stickied", + "reason": "Reason", + "mark_as_read": "mark as read", + "mark_as_unread": "mark as unread", + "delete": "delete", + "deleted": "deleted", + "delete_account": "Delete Account", + "delete_account_confirm": + "Warning: this will permanently delete all your data. Enter your password to confirm.", + "restore": "restore", + "ban": "ban", + "ban_from_site": "ban from site", + "unban": "unban", + "unban_from_site": "unban from site", + "banned": "banned", + "save": "save", + "unsave": "unsave", + "create": "create", + "creator": "creator", + "username": "Username", + "email_or_username": "Email or Username", + "number_of_users": "{{count}} Users", + "number_of_subscribers": "{{count}} Subscribers", + "number_of_points": "{{count}} Points", + "number_online": "{{count}} Users Online", + "name": "Name", + "title": "Title", + "category": "Category", + "subscribers": "Subscribers", + "both": "Both", + "saved": "Saved", + "unsubscribe": "Unsubscribe", + "subscribe": "Subscribe", + "subscribed": "Subscribed", + "prev": "Prev", + "next": "Next", + "sidebar": "Sidebar", + "sort_type": "Sort type", + "hot": "Hot", + "new": "New", + "old": "Old", + "top_day": "Top day", + "week": "Week", + "month": "Month", + "year": "Year", + "all": "All", + "top": "Top", + "api": "API", + "docs": "Docs", + "inbox": "Inbox", + "inbox_for": "Inbox for <1>{{user}}", + "mark_all_as_read": "mark all as read", + "type": "Type", + "unread": "Unread", + "replies": "Replies", + "mentions": "Mentions", + "reply_sent": "Reply sent", + "message_sent": "Message sent", + "search": "Search", + "overview": "Overview", + "view": "View", + "logout": "Logout", + "login_sign_up": "Login / Sign up", + "login": "Login", + "sign_up": "Sign Up", + "notifications_error": + "Desktop notifications not available in your browser. Try Firefox or Chrome.", + "unread_messages": "Unread Messages", + "messages": "Messages", + "password": "Password", + "verify_password": "Verify Password", + "old_password": "Old Password", + "forgot_password": "forgot password", + "reset_password_mail_sent": "Sent an Email to reset your password.", + "password_change": "Password Change", + "new_password": "New Password", + "no_email_setup": "This server hasn't correctly set up email.", + "email": "Email", + "matrix_user_id": "Matrix User", + "private_message_disclaimer": + "Warning: Private messages in Lemmy are not secure. Please create an account on <1>Riot.im for secure messaging.", + "send_notifications_to_email": "Send notifications to Email", + "optional": "Optional", + "expires": "Expires", + "language": "Language", + "browser_default": "Browser Default", + "downvotes_disabled": "Downvotes disabled", + "enable_downvotes": "Enable Downvotes", + "open_registration": "Open Registration", + "registration_closed": "Registration closed", + "enable_nsfw": "Enable NSFW", + "url": "URL", + "body": "Body", + "copy_suggested_title": "copy suggested title: {{title}}", + "community": "Community", + "expand_here": "Expand here", + "subscribe_to_communities": "Subscribe to some <1>communities.", + "chat": "Chat", + "recent_comments": "Recent Comments", + "no_results": "No results.", + "setup": "Setup", + "lemmy_instance_setup": "Lemmy Instance Setup", + "setup_admin": "Set Up Site Administrator", + "your_site": "your site", + "modified": "modified", + "nsfw": "NSFW", + "show_nsfw": "Show NSFW content", + "theme": "Theme", + "sponsors": "Sponsors", + "sponsors_of_lemmy": "Sponsors of 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_liberapay": "Support on Liberapay", + "donate_to_lemmy": "Donate to Lemmy", + "donate": "Donate", + "general_sponsors": + "General Sponsors are those that pledged $10 to $39 to Lemmy.", + "crypto": "Crypto", + "bitcoin": "Bitcoin", + "ethereum": "Ethereum", + "monero": "Monero", + "code": "Code", + "joined": "Joined", + "by": "by", + "to": "to", + "from": "from", + "transfer_community": "transfer community", + "transfer_site": "transfer site", + "are_you_sure": "are you sure?", + "yes": "yes", + "no": "no", + "powered_by": "Powered by", + "landing_0": + "Lemmy is a <1>link aggregator / reddit alternative, intended to work in the <2>fediverse.<3>It's self-hostable, has live-updating comment threads, and is tiny (<4>~80kB). Federation into the ActivityPub network is on the roadmap. <5>This is a <6>very early beta version, and a lot of features are currently broken or missing. <7>Suggest new features or report bugs <8>here.<9>Made with <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.", + "not_logged_in": "Not logged in.", + "logged_in": "Logged in.", + "community_ban": "You have been banned from this community.", + "site_ban": "You have been banned from the site", + "couldnt_create_comment": "Couldn't create comment.", + "couldnt_like_comment": "Couldn't like comment.", + "couldnt_update_comment": "Couldn't update comment.", + "couldnt_save_comment": "Couldn't save comment.", + "couldnt_get_comments": "Couldn't get comments.", + "no_comment_edit_allowed": "Not allowed to edit comment.", + "no_post_edit_allowed": "Not allowed to edit post.", + "no_community_edit_allowed": "Not allowed to edit community.", + "couldnt_find_community": "Couldn't find community.", + "couldnt_update_community": "Couldn't update Community.", + "community_already_exists": "Community already exists.", + "community_moderator_already_exists": "Community moderator already exists.", + "community_follower_already_exists": "Community follower already exists.", + "community_user_already_banned": "Community user already banned.", + "couldnt_create_post": "Couldn't create post.", + "post_title_too_long": "Post title too long.", + "couldnt_like_post": "Couldn't like post.", + "couldnt_find_post": "Couldn't find post.", + "couldnt_get_posts": "Couldn't get posts", + "couldnt_update_post": "Couldn't update post", + "couldnt_save_post": "Couldn't save post.", + "no_slurs": "No slurs.", + "not_an_admin": "Not an admin.", + "site_already_exists": "Site already exists.", + "couldnt_update_site": "Couldn't update site.", + "couldnt_find_that_username_or_email": + "Couldn't find that username or email.", + "password_incorrect": "Password incorrect.", + "passwords_dont_match": "Passwords do not match.", + "admin_already_created": "Sorry, there's already an admin.", + "user_already_exists": "User already exists.", + "email_already_exists": "Email already exists.", + "couldnt_update_user": "Couldn't update user.", + "system_err_login": "System error. Try logging out and back in.", + "couldnt_create_private_message": "Couldn't create private message.", + "no_private_message_edit_allowed": "Not allowed to edit private message.", + "couldnt_update_private_message": "Couldn't update private message.", + "time": "Time", + "action": "Action" +} diff --git a/ui/assets/translations/eo.json b/ui/assets/translations/eo.json new file mode 100644 index 000000000..518e2eb49 --- /dev/null +++ b/ui/assets/translations/eo.json @@ -0,0 +1,175 @@ +{ + "post": "Poŝti", + "remove_post": "Fortiri Poŝton", + "no_posts": "Ne Poŝtoj.", + "create_a_post": "Verki Poŝton", + "create_post": "Verki Poŝton", + "number_of_posts": "{{count}} Poŝtoj", + "posts": "Poŝtoj", + "related_posts": "Tiuj poŝtoj eble rilatas", + "cross_posts": "Tiuj ligilo ankaŭ estas poŝtinta al:", + "cross_post": "laŭapoŝto", + "comments": "Komentoj", + "number_of_comments": "{{count}} Komentoj", + "remove_comment": "Fortiri Komentojn", + "communities": "Komunumoj", + "users": "Uzantoj", + "create_a_community": "Krei komunumon", + "create_community": "Krei Komunumon", + "remove_community": "Forigi Komunumon", + "subscribed_to_communities": "Abonita al <1>komunumoj", + "trending_communities": "Furora <1>komunumoj", + "list_of_communities": "Listo de komunumoj", + "community_reqs": "minusklaj leteroj, substrekoj, kaj ne spacetoj.", + "edit": "redakti", + "reply": "repliki", + "cancel": "nuligi", + "unlock": "malŝlosi", + "lock": "ŝlosi", + "link": "ligi", + "mod": "moderanto", + "mods": "moderantoj", + "moderates": "Moderigas", + "settings": "Agordoj", + "remove_as_mod": "forigi per moderanto", + "appoint_as_mod": "nomumi per moderanto", + "modlog": "Moderlogo", + "admin": "administranto", + "admins": "administrantoj", + "remove_as_admin": "forigi per administranto", + "appoint_as_admin": "nomumi per administranto", + "remove": "fortiri", + "removed": "fortirita", + "locked": "ŝlosita", + "reason": "Kialo", + "mark_as_read": "marki kiel legita", + "mark_as_unread": "marki kiel nelegita", + "delete": "forigi", + "deleted": "forigita", + "restore": "restaŭri", + "ban": "forbari", + "ban_from_site": "forbari de retejo", + "unban": "malforbari", + "unban_from_site": "malforbari de retejo", + "save": "konservi", + "unsave": "malkonservi", + "create": "krei", + "username": "Uzantnomo", + "email_or_username": "Retadreso aŭ Uzantnomo", + "number_of_users": "{{count}} Uzantoj", + "number_of_subscribers": "{{count}} Abonantoj", + "number_of_points": "{{count}} Voĉdonoj", + "name": "Nomo", + "title": "Titolo", + "category": "Kategorio", + "subscribers": "Abonantoj", + "both": "Ambaŭ", + "saved": "Konservita", + "unsubscribe": "Malaboni", + "subscribe": "Aboni", + "subscribed": "Abonita", + "prev": "Antaŭe", + "next": "Poste", + "sidebar": "Flankstango", + "sort_type": "Klasi per kia", + "hot": "Varmaj", + "new": "Novaj", + "top_day": "Supraj tagaj", + "week": "Semajno", + "month": "Monato", + "year": "Jaro", + "all": "Ĉiam", + "top": "Supraj", + "api": "API", + "inbox": "Ricevujo", + "inbox_for": "Ricevujo de <1>{{user}}", + "mark_all_as_read": "marki ĉiujn kiel legitaj", + "type": "Tipo", + "unread": "Nelegitaj", + "reply_sent": "Repliko sendis", + "search": "Serĉi", + "overview": "Resumo", + "view": "Rigardi", + "logout": "Elsaluti", + "login_sign_up": "Ensaluti / Registriĝi", + "login": "Ensaluti", + "sign_up": "Registriĝi", + "notifications_error": + "Labortablaj avizoj estas nehavebla en via retumilo. Provu Firefox-on aŭ Chrome-on.", + "unread_messages": "Nelegitaj Mesaĝoj", + "password": "Pasvorto", + "verify_password": "Konfirmu Vian Pasvorton", + "email": "Retadreso", + "optional": "Fakultativa", + "expires": "Finiĝos", + "url": "URL", + "body": "Ĉefparto", + "copy_suggested_title": "kopii la sugestiitan titolon: {{title}}", + "community": "Komunumo", + "expand_here": "Ekspansii ĉi tie", + "subscribe_to_communities": "Aboni al iuj <1>komunumoj.", + "chat": "Babilo", + "recent_comments": "Freŝaj Komentoj", + "no_results": "Ne rezultoj.", + "setup": "Agordi", + "lemmy_instance_setup": "Agordi Instancon de Lemmy", + "setup_admin": "Agordi Retejan Administranton", + "your_site": "via retejo", + "modified": "modifita", + "nsfw": "NSFW", + "show_nsfw": "Vidigi NSFW-an enhavon", + "sponsors": "Subtenantoj", + "sponsors_of_lemmy": "Subtenantoj de Lemmy", + "sponsor_message": + "Lemmy estas senpaga, <1>liberkoda programaro. Tio signifas ne reklami, pagigi, aŭ riska kapitalo, ĉiam. Viaj donacoj rekte subtenas plentempan evoluon de la projekto. Dankon al tiuj homoj:", + "support_on_patreon": "Subteni per Patreon", + "general_sponsors": + "Ĝeneralaj Subtenantoj estas tiuj ke donacis inter $10 kaj $39 al Lemmy.", + "crypto": "Crypto", + "bitcoin": "Bitcoin", + "ethereum": "Ethereum", + "monero": "Monero", + "code": "Kodo", + "joined": "Unuiĝis", + "by": "de", + "to": "al", + "transfer_community": "transdoni la komunumon", + "transfer_site": "transdoni la retejon", + "powered_by": "Konstruis per", + "landing_0": + "Lemmy estas <1>ligila agregatilo / Reddit anstataŭo ke intenciĝas funkci en la <2>federacio-universo.<3>ĝi estas mem-gastigebla, havas nuna-ĝisdatigajn komentarojn, kaj estas malgrandega (<4>~80kB). Federacio en la ActivityPub-an reton estas planizita. <5>Estas <6>fruega beta versio, kaj multaj trajtoj estas nune difektaj aŭ mankaj. <7>Sugestias novajn trajtojn aŭ raportas cimojn <8>ĉi tie.<9>Faris per <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.", + "not_logged_in": "Ne estas ensalutinta.", + "community_ban": "Vi estas forbarita de la komunumo.", + "site_ban": "Vi estas forbarita de la retejo", + "couldnt_create_comment": "Ne povis krei la komenton.", + "couldnt_like_comment": "Ne povis ŝati la komenton.", + "couldnt_update_comment": "Ne povis ĝisdatigi komenton.", + "couldnt_save_comment": "Ne povis konservi komenton.", + "no_comment_edit_allowed": "Ne rajtas redakti la komenton.", + "no_post_edit_allowed": "Ne rajtas redakti la poŝton.", + "no_community_edit_allowed": "Ne rajtas redakti la komunumon.", + "couldnt_find_community": "Ne povis trovi la komunumon.", + "couldnt_update_community": "Ne povis ĝisdatigi la komunumon.", + "community_already_exists": "Komunumo jam ekzistas.", + "community_moderator_already_exists": "Komunuma moderanto jam ekzistas.", + "community_follower_already_exists": "Komunuma sekvanto.", + "community_user_already_banned": "Komunuma uzanto jam estas forbarita.", + "couldnt_create_post": "Ne povis krei la poŝton.", + "couldnt_like_post": "Ne povis ŝati la poŝton.", + "couldnt_find_post": "Ne povis trovi la poŝton.", + "couldnt_get_posts": "Ne povis irpreni poŝtojn", + "couldnt_update_post": "Ne povis ĝisdatigi la poŝton", + "couldnt_save_post": "Ne povis konservi la poŝton.", + "no_slurs": "Ne bigotaj vortoj.", + "not_an_admin": "Ne estas administranto.", + "site_already_exists": "Retejo jam ekzistas.", + "couldnt_update_site": "Ne povis ĝisdatigi la retejon.", + "couldnt_find_that_username_or_email": + "Ne povis trovi tiun uzantnomon aŭ retadreson.", + "password_incorrect": "Pasvorto malĝustas.", + "passwords_dont_match": "Pasvortoj ne samas.", + "admin_already_created": "Pardonu, jam estas administranto.", + "user_already_exists": "Uzanto jam ekzistas.", + "couldnt_update_user": "Ne povis ĝisdatigi la uzanton.", + "system_err_login": "Sistema eraro. Provu elsaluti kaj ensaluti." +} diff --git a/ui/assets/translations/es.json b/ui/assets/translations/es.json new file mode 100644 index 000000000..61bebb971 --- /dev/null +++ b/ui/assets/translations/es.json @@ -0,0 +1,240 @@ +{ + "post": "Publicar", + "remove_post": "Eliminar publicación", + "no_posts": "Sin publicaciones.", + "create_a_post": "Crear una publicación", + "create_post": "Crear Publicación", + "number_of_posts": "{{count}} Publicaciones", + "posts": "Publicaciones", + "related_posts": "Estas publicaciones podrían estar relacionadas", + "cross_posts": "Este link también ha sido publicado en:", + "cross_post": "cross-post", + "comments": "Comentarios", + "number_of_comments": "{{count}} Comentarios", + "remove_comment": "Eliminar Comentarios", + "communities": "Comunidades", + "users": "Usuarios", + "create_a_community": "Crear una comunidad", + "create_community": "Crear Comunidad", + "remove_community": "Eliminar Comunidad", + "subscribed_to_communities": "Suscrito a <1>comunidades", + "trending_communities": "<1>Comunidades en tendencia", + "list_of_communities": "Lista de comunidades", + "number_of_communities": "{{count}} Comunidades", + "community_reqs": "minúsculas, guión bajo, y sin espacios.", + "create_private_message": "Crear Mensaje Privado", + "send_secure_message": "Enviar Mensaje Seguro", + "send_message": "Enviar Mensaje", + "message": "Mensaje", + "edit": "editar", + "reply": "responder", + "cancel": "Cancelar", + "preview": "Previsualizar", + "upload_image": "subir imagen", + "avatar": "Avatar", + "upload_avatar": "Subir Avatar", + "show_avatars": "Ver Avatares", + "formatting_help": "Ayuda de formato", + "view_source": "ver fuente", + "unlock": "desbloquear", + "lock": "bloquear", + "sticky": "fijado", + "unsticky": "no fijado", + "link": "link", + "archive_link": "archivar link", + "mod": "moderador", + "mods": "moderadores", + "moderates": "Modera", + "settings": "Configuración", + "remove_as_mod": "eliminar como moderador", + "appoint_as_mod": "designar como moderador", + "modlog": "Historial de moderación", + "admin": "administrador", + "admins": "administradores", + "remove_as_admin": "eliminar como administrador", + "appoint_as_admin": "designar como administrador", + "remove": "eliminar", + "removed": "eliminado", + "locked": "bloqueado", + "stickied": "fijado", + "reason": "Razón", + "mark_as_read": "marcar como leído", + "mark_as_unread": "marcar como no leído", + "delete": "eliminar", + "deleted": "eliminado", + "delete_account": "Eliminar Cuenta", + "delete_account_confirm": + "Aviso: esta acción eliminará permanentemente tu información. Introduce tu contraseña para continuar", + "restore": "restaurar", + "ban": "expulsar", + "ban_from_site": "expulsar del sitio", + "unban": "admitir", + "unban_from_site": "admitir en el sitio", + "banned": "expulsado", + "save": "guardar", + "unsave": "descartar", + "create": "crear", + "creator": "creador", + "username": "Nombre de Usuario", + "email_or_username": "Correo o Usuario", + "number_of_users": "{{count}} Usuarios", + "number_of_subscribers": "{{count}} Suscriptores", + "number_of_points": "{{count}} Puntos", + "number_online": "{{count}} Usuarios En Línea", + "name": "Nombre", + "title": "Titulo", + "category": "Categoría", + "subscribers": "Suscriptores", + "both": "Ambos", + "saved": "Guardado", + "unsubscribe": "Desuscribirse", + "subscribe": "Suscribirse", + "subscribed": "Suscrito", + "prev": "Anterior", + "next": "Siguiente", + "sidebar": "Descripción de la comunidad", + "sort_type": "Tipo de orden", + "hot": "Popular", + "new": "Nuevo", + "top_day": "Lo mejor del día", + "week": "Semana", + "month": "Mes", + "year": "Año", + "all": "Todo", + "top": "Mejor", + "api": "API", + "docs": "Docs", + "inbox": "Buzón de entrada", + "inbox_for": "Buzón de entrada para <1>{{user}}", + "mark_all_as_read": "marcar todo como leído", + "type": "Tipo", + "unread": "No leído", + "replies": "Respuestas", + "mentions": "Menciones", + "reply_sent": "Respuesta enviada", + "message_sent": "Mensaje enviado", + "search": "Buscar", + "overview": "Resumen", + "view": "Vista", + "logout": "Cerrar sesión", + "login_sign_up": "Iniciar sesión / Crear cuenta", + "login": "Iniciar sesión", + "sign_up": "Crear cuenta", + "notifications_error": + "Notificaciones de escritorio no disponibles en tu navegador. Prueba Firefox o Chrome.", + "unread_messages": "Mensajes no leídos", + "messages": "Mensajes", + "password": "Contraseña", + "verify_password": "Verificar contraseña", + "old_password": "Antigua Contraseña", + "forgot_password": "olvidé mi contraseña", + "reset_password_mail_sent": "Enviar correo para reestablecer la contraseña.", + "password_change": "Cambio de Contraseña", + "new_password": "Nueva Contraseña", + "no_email_setup": "Este servidor no ha activado correctamente el correo.", + "email": "Correo electrónico", + "matrix_user_id": "Usuario Matricial", + "private_message_disclaimer": + "Aviso: Los mensajes privados en Lemmy no son seguros. Por favor cree una cuenta en <1>Riot.im para mensajeria segura.", + "send_notifications_to_email": "Enviar notificaciones al correo", + "optional": "Opcional", + "expires": "Expira", + "language": "Idioma", + "browser_default": "Por defecto del navegador", + "downvotes_disabled": "Votos negativos deshabilitados", + "enable_downvotes": "Habilitar votos negativos", + "open_registration": "Abrir registro", + "registration_closed": "Registro cerrado", + "enable_nsfw": "Habilitar NSFW", + "url": "URL", + "body": "Descripción", + "copy_suggested_title": "Copiar el título sugerido: {{title}}", + "community": "Comunidad", + "expand_here": "Expandir aquí", + "subscribe_to_communities": "Suscribirse a algunas <1>comunidades.", + "chat": "Chat", + "recent_comments": "Comentarios recientes", + "no_results": "Sin resultados.", + "setup": "Configurar", + "lemmy_instance_setup": "Configuración de instancia de Lemmy", + "setup_admin": "Configurar administrador del Sitio", + "your_site": "tu sitio", + "modified": "modificado", + "nsfw": "NSFW", + "show_nsfw": "Mostrar contenido NSFW", + "theme": "Tema", + "sponsors": "Patrocinadores", + "sponsors_of_lemmy": "Patrocinadores de Lemmy", + "sponsor_message": + "Lemmy es software libre y de <1>código abierto, lo que significa que no tendrá publicidades, monetización, ni capitales emprendedores, nunca. Tus donaciones apoyan directamente el desarrollo a tiempo completo del proyecto. Muchas gracias a las siguientes personas:", + "support_on_patreon": "Apoyo en Patreon", + "support_on_liberapay": "Apoyo en Liberapay", + "donate_to_lemmy": "Donar a Lemmy", + "donate": "Donar", + "general_sponsors": + "Los Patrocinadores Generales son aquellos que señaron entre $10 y $39 a Lemmy.", + "crypto": "Crypto", + "bitcoin": "Bitcoin", + "ethereum": "Ethereum", + "monero": "Monero", + "code": "Código", + "joined": "Se unió", + "by": "por", + "to": "a", + "from": "desde", + "transfer_community": "transferir comunidad", + "transfer_site": "transferir sitio", + "are_you_sure": "¿Estás seguro?", + "yes": "sí", + "no": "no", + "powered_by": "Impulsado por", + "landing_0": + "Lemmy es un <1>agregador de links / alternativa a reddit, con la intención de funcionar en el <2>fediverso.<3>Es alojable por uno mismo (sin necesidad de grandes compañías), tiene actualización en vivo de cadenas de comentarios, y es pequeño (<4>~80kB). Federar con el sistema de redes ActivityPub forma parte de los objetivos del proyecto. <5>Esta es una <6>version beta muy prematura, y actualmente muchas de las características están rotas o faltan. <7>Sugiere nuevas características o reporta errores <8>aquí.<9>Hecho con <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.", + "not_logged_in": "No has iniciado sesión.", + "logged_in": "Has iniciado sesión.", + "community_ban": "Has sido expulsado de esta comunidad.", + "site_ban": "Has sido expulsado del sitio", + "couldnt_create_comment": "No se pudo crear el comentario.", + "couldnt_like_comment": "No se pudo dar me gusta al comentario.", + "couldnt_update_comment": "No se pudo actualizar el comentario.", + "couldnt_save_comment": "No se pudo guardar el comentario.", + "no_comment_edit_allowed": "No tiene permisos para editar el comentario.", + "no_post_edit_allowed": "No tiene permisos para editar la publicación.", + "no_community_edit_allowed": "No tiene permisos para editar la comunidad.", + "couldnt_find_community": "No se pudo encontrar la comunidad.", + "couldnt_update_community": "No se pudo actualizar la comunidad.", + "community_already_exists": "Esta comunidad ya existe.", + "community_moderator_already_exists": + "Este moderador de la comunidad ya existe.", + "community_follower_already_exists": + "Este seguidor de la comunidad ya existe.", + "community_user_already_banned": + "Este usuario de la comunidad ya fue expulsado.", + "couldnt_create_post": "No se pudo crear la publicación.", + "couldnt_like_post": "No se pudo gustar la publicación.", + "couldnt_find_post": "No se pudo encontrar la publicación.", + "couldnt_get_posts": "No se pudo obtener las publicaciones", + "couldnt_update_post": "No se pudo actualizar la publicación", + "couldnt_save_post": "No se pudo guardar la publicación.", + "no_slurs": "Prohibido insultar.", + "not_an_admin": "No es un administrador.", + "site_already_exists": "El sitio ya existe.", + "couldnt_update_site": "No se pudo actualizar el sitio.", + "couldnt_find_that_username_or_email": + "No se pudo encontrar ese nombre de usuario o correo electrónico.", + "password_incorrect": "Contraseña incorrecta.", + "passwords_dont_match": "Las contraseñas no coinciden.", + "admin_already_created": "Lo sentimos, ya hay un adminisitrador.", + "user_already_exists": "El usuario ya existe.", + "email_already_exists": "El correo ya está en uso.", + "couldnt_update_user": "No se pudo actualizar el usuario.", + "system_err_login": + "Error del sistema. Intente cerrar sesión e ingresar de nuevo.", + "couldnt_create_private_message": "No se pudo crear el mensaje privado.", + "no_private_message_edit_allowed": + "Sin permisos para editar el mensaje privado.", + "couldnt_update_private_message": "No se pudo actualizar el mensaje privado.", + "old": "Antiguo", + "time": "Tiempo", + "action": "Acción" +} diff --git a/ui/assets/translations/fa.json b/ui/assets/translations/fa.json new file mode 100644 index 000000000..c72c02190 --- /dev/null +++ b/ui/assets/translations/fa.json @@ -0,0 +1,167 @@ +{ + "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/assets/translations/fi.json b/ui/assets/translations/fi.json new file mode 100644 index 000000000..c766cacd0 --- /dev/null +++ b/ui/assets/translations/fi.json @@ -0,0 +1,235 @@ +{ + "translation": { + "post": "viesti", + "remove_post": "Poista viesti", + "no_posts": "Ei viestjä.", + "create_a_post": "Luo viesti", + "create_post": "Luo viesti", + "number_of_posts": "{{count}} viestiä", + "posts": "Viestit", + "related_posts": "Nämä viestit voivat liittyä toisiinsa", + "cross_posts": "Tämä linkki on jaettu:", + "cross_post": "jaa ristiin", + "comments": "Kommentit", + "number_of_comments": "{{count}} kommenttia", + "remove_comment": "Poista kommentti", + "communities": "Yhteisöt", + "users": "Käyttäjät", + "create_a_community": "Luo yhteisö", + "create_community": "Luo yhteisö", + "remove_community": "Poista yhteisö", + "subscribed_to_communities": "Tilatut <1>yhteisöt", + "trending_communities": "Nousevat <1>yhteisöt", + "list_of_communities": "Lista yhteisöistä", + "number_of_communities": "{{count}} yhteisöä", + "community_reqs": + "pienillä kirjaimilla, alleviivauksella, eikä välilyöntejä.", + "create_private_message": "Luo yksityisviesti", + "send_secure_message": "Lähetä suojattu viesti", + "send_message": "Lähetä viesti", + "message": "Viesti", + "edit": "muokkaa", + "reply": "vastaa", + "cancel": "Peru", + "preview": "Esikatselu", + "upload_image": "lataa kuva", + "avatar": "avatar", + "upload_avatar": "Lähetä avatar", + "show_avatars": "Näytä avatarit", + "formatting_help": "apua muotoiluun", + "view_source": "näytä lähde", + "unlock": "avaa", + "lock": "lukitse", + "sticky": "kiinnitä", + "unsticky": "poista kiinnitys", + "link": "linkitä", + "archive_link": "arkistoi linkki", + "mod": "moderaattori", + "mods": "moderaattorit", + "moderates": "Moderoi", + "settings": "Asetukset", + "remove_as_mod": "Poista moderaattorina", + "appoint_as_mod": "Nimitä moderaattoriksi", + "modlog": "Moderoinnin loki", + "admin": "Ylläpitäjä", + "admins": "ylläpitäjät", + "remove_as_admin": "poista ylläpitäjänä", + "appoint_as_admin": "nimitä ylläpitäjäksi", + "remove": "poista", + "removed": "poistettu", + "locked": "lukittu", + "stickied": "kiinnitetty", + "reason": "Syy", + "mark_as_read": "merkitse luetuksi", + "mark_as_unread": "merkitse lukemattomaksi", + "delete": "poista", + "deleted": "deleted", + "delete_account": "Poista tili", + "delete_account_confirm": + "Varoitus: tämä poistaa pysyvästi kaiken datasi. Anna salasanasi varmistukseksi.", + "restore": "palauta", + "ban": "porttikielto", + "ban_from_site": "aseta porttikielto sivulle", + "unban": "poista porttikielto", + "unban_from_site": "poista porttikielto sivulta", + "banned": "asetettu porttikieltoon", + "save": "tallenna", + "unsave": "jätä tallentamatta", + "create": "luo", + "creator": "luoja", + "username": "Käyttäjänimi", + "email_or_username": "Sähköposti tai käyttäjätunnus", + "number_of_users": "{{count}} käyttäjää", + "number_of_subscribers": "{{count}} tilaajaa", + "number_of_points": "{{count}} pistettä", + "number_online": "{{count}} käyttäjää aktiivisena", + "name": "Nimi", + "title": "Kuvaus", + "category": "Luokka", + "subscribers": "Tilaajat", + "both": "Molemmat", + "saved": "Tallennettu", + "unsubscribe": "Poista tilaus", + "subscribe": "Tilaa", + "subscribed": "Tilattu", + "prev": "Edellinen", + "next": "Seuraava", + "sidebar": "Sivupalkki", + "sort_type": "Lajittele tyypin mukaan", + "hot": "Kuumat", + "new": "Uudet", + "top_day": "Päivän parhaimmat", + "week": "Viikko", + "month": "Kuukausi", + "year": "Vuosi", + "all": "Kaikki", + "top": "Parhaimmat", + "api": "API", + "docs": "Dokumentaatio", + "inbox": "Postilaatikko", + "inbox_for": "Postilaatikko käyttäjällä <1>{{user}}", + "mark_all_as_read": "aseta kaikki luetuiksi", + "type": "Tyyppi", + "unread": "Lukematon", + "replies": "Vastaukset", + "mentions": "Maininnat", + "reply_sent": "Vastaus lähetetty", + "message_sent": "Viesti lähetetty", + "search": "Etsi", + "overview": "Yleiskatsaus", + "view": "Katso", + "logout": "Kirjaudu ulos", + "login_sign_up": "Kirjaudu sisään / Rekisteröidy", + "login": "Kirjaudu sisään", + "sign_up": "Rekisteröidy", + "notifications_error": + "Työpöydän ilmoitukset eivät ole saatavilla selaimellesi. Yritä Firefoxia tai Chromea.", + "unread_messages": "Lukemattomat viestit", + "messages": "Viestit", + "password": "Salasana", + "verify_password": "Vahvista salasana", + "old_password": "Vanha salasana", + "forgot_password": "unohdin salasanani", + "reset_password_mail_sent": "Sähköposti lähetettiin salasanan nollaamiseksi.", + "password_change": "Salasanan muutos", + "new_password": "Uusi salasana", + "no_email_setup": "Tämä palvelin ei ole asettanut sähköpostia oikein.", + "email": "Sähköposti", + "matrix_user_id": " Matrix-käyttäjä", + "private_message_disclaimer": + "Varoitus: Yksityisviestit Lemmyssä eivät ole turvallisia. Luo tili <1>Riot.im -palveluun turvallista viestintää varten.", + "send_notifications_to_email": "Lähetä ilmoitukset sähköpostiin", + "optional": "Valinnainen", + "expires": "Umpeutuu", + "language": "Kieli", + "browser_default": "Selaimen oletus", + "downvotes_disabled": "Alaäänet otettu pois päältä", + "enable_downvotes": "Salli alaäänet", + "open_registration": "Avaa rekisteröityminen", + "registration_closed": "Rekisteröityminen suljettu", + "enable_nsfw": "Salli NSFW", + "url": "URL", + "body": "Body", + "copy_suggested_title": "kopioi ehdotettu otsikko: {{title}}", + "community": "Yhteisö", + "expand_here": "Laajenna tässä", + "subscribe_to_communities": "Tilaa joitakin <1>yhteisöjä.", + "chat": "Chat", + "recent_comments": "Viimeaikaiset kommentit", + "no_results": "Ei tuloksia.", + "setup": "Asetus", + "lemmy_instance_setup": "Lemmy-instanssin asetus", + "setup_admin": "Aseta sivuston ylläpitäjä", + "your_site": "sivustosi", + "modified": "muokattu", + "nsfw": "NSFW", + "show_nsfw": "Näytä NSFW-sisältö", + "theme": "Teema", + "sponsors": "Sponsorit", + "sponsors_of_lemmy": "Lemmy-sponsorit", + "sponsor_message": + "Lemmy on vapaa, <1>avoimen lähdekoodin -ohjelmisto, eli mainontaa, rahantekemistä, tai pääomasijoitusta täällä ei tule ikinä olemaan. Lahjoituksesi tukevat suoraan projektin täysipäiväistä kehitystä. Kiitokset seuraaville ihmisille:", + "support_on_patreon": "Tue Patreonissa", + "donate_to_lemmy": "Lahjoita Lemmylle", + "donate": "Lahjoita", + "general_sponsors": + "Yleisiä sponsoreja ovat he, jotka lupaavat 10-39 dollaria Lemmylle.", + "crypto": "Crypto", + "bitcoin": "Bitcoin", + "ethereum": "Ethereum", + "monero": "Monero", + "code": "Code", + "joined": "Liittyi", + "by": "käyttäjältä", + "to": "yhteisössä", + "from": "paikasta", + "transfer_community": "siirron yhteisö", + "transfer_site": "siirron määrä", + "are_you_sure": "oletko varma?", + "yes": "kyllä", + "no": "ei", + "powered_by": "Vauhdittajana", + "landing_0": + "Lemmy on <1>linkinkerääjä / Reddit-vaihtoehto, tarkoitettu toimimaan <2>fediversessä.<3>Sitä voi isännöidä itse, siinä on tosiaikaisesti päivittyvät kommenttiketjut, ja se on pieni (<4>~80 kilotavua). Federointi ActivityPub-verkkoon on suunnittelun alla. <5>Tämä on <6>hyvin varhainen betaversio, ja monet ominaisuudet ovat toistaiseksi rikki tai poissa. <7>Ehdota uusia ominaisuuksia tai raportoi bugeja <8>tänne.<9>Tehty teknologioilla <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.", + "not_logged_in": "Ei kirjautunut sisään.", + "logged_in": "Kirjautunut sisään.", + "community_ban": "Sinulle on asetettu porttikielto tähän yhteisöön.", + "site_ban": "Sinut on asetettu porttikieltoon tältä sivustolta", + "couldnt_create_comment": "Kommenttia ei pystytty luomaan.", + "couldnt_like_comment": "Kommentista ei voitu tykätä.", + "couldnt_update_comment": "Kommenttia ei voitu päivittää.", + "couldnt_save_comment": "Kommenttia ei voitu tallentaa.", + "no_comment_edit_allowed": "Et ole sallittu muokkaamaan kommenttia.", + "no_post_edit_allowed": "Et ole sallittu muokkaamaan viestiä.", + "no_community_edit_allowed": "Et ole sallittu muokkaamaan yhteisöä.", + "couldnt_find_community": "Yhteisöä ei voitu löytää.", + "couldnt_update_community": "Yhteisöä ei voitu päivittää.", + "community_already_exists": "Yhteisö on jo olemassa.", + "community_moderator_already_exists": "Yhteisön moderaattori on jo olemassa.", + "community_follower_already_exists": "Yhteisön seuraaja on jo olemassa.", + "community_user_already_banned": "Yhteisön käyttäjä on jo porttikiellossa.", + "couldnt_create_post": "Ei voitu luoda viestiä.", + "couldnt_like_post": "Viestistä ei voitu tykätä.", + "couldnt_find_post": "Viestiä ei löytynyt.", + "couldnt_get_posts": "Viestejä ei saatu", + "couldnt_update_post": "Viestiä ei voitu päivittää", + "couldnt_save_post": "Viestiä ei voitu tallentaa.", + "no_slurs": "Ei loukkauksia.", + "not_an_admin": "Ei ole ylläpitäjä.", + "site_already_exists": "Sivusto on jo olemassa.", + "couldnt_update_site": "Sivustoa ei voitu päivittää.", + "couldnt_find_that_username_or_email": + "Käyttäjänimeä tai sähköpostia ei onnistuttu löytämään.", + "password_incorrect": "Salasana on väärin.", + "passwords_dont_match": "Salasanat eivät täsmää.", + "admin_already_created": "Anteeksi, mutta täällä on jo ylläpitäjä.", + "user_already_exists": "Käyttäjä on jo olemassa.", + "email_already_exists": "Sähköposti on jo olemassa.", + "couldnt_update_user": "Käyttäjää ei voitu päivittää.", + "system_err_login": + "Järjestelmävirhe. Yritä kirjautua ulos ja kirjautua uudestaan sisään.", + "couldnt_create_private_message": "Yksityisviestiä ei voitu luoda.", + "no_private_message_edit_allowed": + "Et ole sallittu muokkaamaan yksityisviestiä.", + "couldnt_update_private_message": "Yksityisviestiä ei voitu päivittää." +} diff --git a/ui/assets/translations/fr.json b/ui/assets/translations/fr.json new file mode 100644 index 000000000..5f07d73ae --- /dev/null +++ b/ui/assets/translations/fr.json @@ -0,0 +1,240 @@ +{ + "post": "publication", + "remove_post": "Supprimer la publication", + "no_posts": "Pas de publications.", + "create_a_post": "Créer une publication", + "create_post": "Créer la publication", + "number_of_posts": "{{count}} Publications", + "posts": "Publications", + "related_posts": "Ces sujets peuvent être corrélés", + "cross_posts": "Ce sujet a également été posté sur :", + "cross_post": "publication croisée", + "cross_posted_to": "publication croisée à", + "comments": "Commentaires", + "number_of_comments": "{{count}} Commentaires", + "remove_comment": "Supprimer le commentaire", + "communities": "Communautés", + "users": "Utilisateurs", + "create_a_community": "Créer une communauté", + "create_community": "Créer la communauté", + "remove_community": "Supprimer la Communauté", + "subscribed_to_communities": "Abonné à ces <1>communautés", + "trending_communities": "<1>Communautés appréciées", + "list_of_communities": "Liste des communautés", + "number_of_communities": "{{count}} communautés", + "community_reqs": "en minuscule, sans espace et avec tiret du bas.", + "create_private_message": "Créer un message privé", + "send_secure_message": "Envoyer le message sécurisé", + "send_message": "Enovyer le message", + "message": "Message", + "edit": "éditer", + "reply": "répondre", + "cancel": "Annuler", + "preview": "prévisualiser", + "upload_image": "envoyer une image", + "avatar": "Avatar", + "upload_avatar": "Télécharger une avatar", + "show_avatars": "Afficher les avatars", + "formatting_help": "aide au formattage", + "view_source": "voir la source", + "unlock": "débloquer", + "lock": "bloquer", + "sticky": "épingler", + "unsticky": "décrocher", + "link": "lien", + "archive_link": "archiver le lien", + "mod": "modérateur", + "mods": "modérateurs", + "moderates": "Modérer", + "settings": "Paramètres", + "remove_as_mod": "Supprimer comme modérateur", + "appoint_as_mod": "Nommer comme modérateur", + "modlog": "Historique de modération", + "admin": "admin", + "admins": "admins", + "remove_as_admin": "Supprimer comme admin", + "appoint_as_admin": "Nommer comme admin", + "remove": "retirer", + "removed": "retiré", + "locked": "bloqué", + "stickied": "épinglé", + "reason": "Raison", + "mark_as_read": "marquer comme lu", + "mark_as_unread": "marquer comme non-lu", + "delete": "supprimer", + "deleted": "supprimé", + "delete_account": "Supprimer le compte", + "delete_account_confirm": + "Attention: cette action supprime toutes vos données de façons permanente ! Entrez votre mot de passe pour confirmer.", + "restore": "restaurer", + "ban": "bannir", + "ban_from_site": "bannir du site", + "unban": "pardon", + "unban_from_site": "faire revenir sur le site", + "banned": "banni", + "save": "sauvegarder", + "unsave": "retirer", + "create": "créer", + "creator": "createur", + "username": "Nom d'utilisateur", + "email_or_username": "Email ou Nom d'utilisateur", + "number_of_users": "{{count}} Utilisateurs", + "number_of_subscribers": "{{count}} Abonnés", + "number_of_points": "{{count}} Points", + "number_online": "{{count}} Utilisateurs en ligne", + "name": "Nom", + "title": "Titre", + "category": "Catégorie", + "subscribers": "Abonnés", + "both": "Les deux", + "saved": "Sauvegardé", + "unsubscribe": "Se désabonner", + "subscribe": "S'abonner", + "subscribed": "Abonnés", + "prev": "Précédent", + "next": "Suivant", + "sidebar": "Texte latéral", + "sort_type": "Trier", + "hot": "Tendances", + "new": "Nouveaux", + "old": "Ancien", + "top_day": "Top du jour", + "week": "Semaine", + "month": "Mois", + "year": "Année", + "all": "Tout", + "top": "Top", + "api": "API", + "docs": "Documentations", + "inbox": "Boîte de réception", + "inbox_for": "Boîte de réception de <1>{{user}}", + "mark_all_as_read": "Tout marquer comme lu", + "type": "Type", + "unread": "Non-lu", + "replies": "Réponses", + "mentions": "Mentions", + "reply_sent": "Réponse envoyée", + "message_sent": "Message envoyé", + "search": "Rechercher", + "overview": "Général", + "view": "Voir", + "logout": "Se déconnecter", + "login_sign_up": "Se connecter / S'inscrire", + "login": "Se connecter", + "sign_up": "S'inscrire", + "notifications_error": + "Les notifications de bureau ne sont pas discponibles sur votre navigateur. Essayez Firefox ou Chrome.", + "unread_messages": "Messages non-lu", + "messages": "Messages", + "password": "Mot de passe", + "verify_password": "Vérifiez le mot de passe", + "old_password": "Ancien mot de passe", + "forgot_password": "Mot de passe oublié", + "reset_password_mail_sent": "Un email a été envoyé pour réinitialiser votre mot de passe.", + "password_change": "Changement de mot de passe", + "new_password": "Nouveau mot de passe", + "no_email_setup": "Ce serveur n'a pas correctement configuré la messagerie de email.", + "email": "Email", + "matrix_user_id": "Utilisateur Matrix", + "private_message_disclaimer": + "Attention: les messages privés en Matrix ne sont pas sécurisés. S'il vous plait, créer un compte de <1>Riot.im pour des messages sécurisés.", + "send_notifications_to_email": "Envoyer des notifications par email", + "optional": "Optionnel", + "expires": "Expire", + "language": "Langue", + "browser_default": "Défaut pour le navigateur", + "downvotes_disabled": "Votes négatifs désactivés", + "enable_downvotes": "Votes négatifs activés", + "open_registration": "Ouvrir la regestration", + "registration_closed": "Régestration fermée", + "enable_nsfw": "Activer NSFW", + "url": "URL", + "body": "Texte", + "copy_suggested_title": "Ajouter le titre suggéré: {{title}}", + "community": "Communauté", + "expand_here": "Développer ici", + "subscribe_to_communities": "S'abonner à quelques <1>communautés.", + "chat": "Chat", + "recent_comments": "Commentaires récents", + "no_results": "Pas de résultats.", + "setup": "Installation", + "lemmy_instance_setup": "Installation d'une instance Lemmy", + "setup_admin": "Créer un administrateur", + "your_site": "votre site", + "modified": "modifié", + "nsfw": "Pas sûr pour le travail", + "show_nsfw": "Afficher le contenu NSFW", + "theme": "Thème", + "sponsors": "Sponsors", + "sponsors_of_lemmy": "Sponsors de Lemmy", + "sponsor_message": + "Lemmy est gratuit et <1>open-source, c'est à dire sans publicité et sans monétisation. Pour toujours. Vos dons soutiennent directement le développement du projet. Merci à nos soutiens.", + "support_on_patreon": "Soutenir sur Patreon", + "support_on_liberapay": "Soutenir sur Liberapay", + "donate_to_lemmy": "Faire un don à Lemmy", + "donate": "Faire un don", + "general_sponsors": "Les sponsors généraux sont ceux garantissant de 10 à 39$.", + "crypto": "Cryptomonnaies", + "bitcoin": "Bitcoin", + "ethereum": "Ethereum", + "monero": "Monero", + "code": "Code", + "joined": "Membre depuis", + "by": "par", + "to": "vers", + "from": "de", + "transfer_community": "transférer la communauté", + "transfer_site": "transférer le site", + "are_you_sure": "Êtes-vous sûr ?", + "yes": "oui", + "no": "non", + "powered_by": "Propulsé par", + "landing_0": + "Lemmy est un <1>aggrégateur de lien, similaire à reddit et conçu pour fonctionner sur le <2>fédiverse.<3>Il est auto-hébergeable, se met à jour en direct et est léger (<4>~80kB). La fédération via Activitypub est prévue. <5>Lemmy est une <6>version beta très précoce, et de nombreuses fonctionnalités sont manquantes ou non fonctionnelles. <7>Vous pouvez rapporter des bugs et suggérez de nouvelles fonctionnalités <8>ici.<9>Crée avec <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.", + "not_logged_in": "Vous n'êtes pas connecté.", + "logged_in": "Vous êtes connecté.", + "community_ban": "Vous avez été banni de cette communauté.", + "site_ban": "Vous avez été banni du site", + "couldnt_create_comment": "Impossible de poster le commentaire.", + "couldnt_like_comment": "Impossible d'aimer le commentaire.", + "couldnt_update_comment": "Impossible de mettre à jour le commentaire.", + "couldnt_save_comment": "Impossible de sauvegarder le commentaire.", + "couldnt_get_comments": "Impossible de obtenir les commentaires.", + "no_comment_edit_allowed": + "Vous n'êtes pas autorisé à éditer ce commentaire.", + "no_post_edit_allowed": "Vous n'êtes pas autorisé à éditer sujet.", + "no_community_edit_allowed": + "Vous n'êtes pas autorisé à éditer cette communauté.", + "couldnt_find_community": "Impossible de trouver cette communauté.", + "couldnt_update_community": "Impossible d'éditer cette communauté.", + "community_already_exists": "Cette communauté existe déjà.", + "community_moderator_already_exists": "Ce membre est déjà modérateur.", + "community_follower_already_exists": "Ce membre est déjà abonné.", + "community_user_already_banned": "Ce membre est déjà banni.", + "couldnt_create_post": "Impossible de créer le sujet.", + "post_title_too_long": "Sujet titre trop long.", + "couldnt_like_post": "Impossible d'aimer le sujet.", + "couldnt_find_post": "Impossible de trouver le sujet.", + "couldnt_get_posts": "Impossible d'obtenir les sujets", + "couldnt_update_post": "Impossible de mettre à jour le sujet", + "couldnt_save_post": "Impossible de sauvegarder le sujet.", + "no_slurs": "Pas d'insultes.", + "not_an_admin": "Pas administrateur.", + "site_already_exists": "Le site existe déjà.", + "couldnt_update_site": "Impossible de mettre à jour le site.", + "couldnt_find_that_username_or_email": + "Impossible de trouver cet utilisateur ou cet email.", + "password_incorrect": "Mot de passe incorrect.", + "passwords_dont_match": "Les mots de passes ne correspondent pas..", + "admin_already_created": "Désolé, il y a déjà un admin.", + "user_already_exists": "L'utilisateur existe déjà.", + "email_already_exists": "L'email existe déjà", + "couldnt_update_user": "Impossible de mettre à jour l'utilisateur.", + "system_err_login": + "Erreur système. Essayez de vous déconneter puis de vous reconnecter.", + "couldnt_create_private_message": "Impossible de créer un message privé.", + "no_private_message_edit_allowed": "Pas autorisé à modifier un message privé.", + "couldnt_update_private_message": "Impossible de modifier un message privé.", + "time": "Temps", + "action": "Action" +} diff --git a/ui/assets/translations/it.json b/ui/assets/translations/it.json new file mode 100644 index 000000000..50e621d93 --- /dev/null +++ b/ui/assets/translations/it.json @@ -0,0 +1,190 @@ +{ + "post": "post", + "remove_post": "Rimuovi Post", + "no_posts": "Nessun Post.", + "create_a_post": "Crea un post", + "create_post": "Crea Post", + "number_of_posts": "{{count}} Posts", + "posts": "Posts", + "related_posts": "Questi post potrebbero essere correlati", + "cross_posts": "Questo link è stato postato anche in:", + "cross_post": "cross-post", + "comments": "Commenti", + "number_of_comments": "{{count}} Commenti", + "remove_comment": "Rimuovi Commento", + "communities": "Comunità", + "users": "Utenti", + "create_a_community": "Crea una Comunità", + "create_community": "Crea Comunità", + "remove_community": "Rimuovi Comunità", + "subscribed_to_communities": "Iscritto alle <1>comunità", + "trending_communities": "<1>Comunità in crescita", + "list_of_communities": "Lista di comunità", + "number_of_communities": "{{count}} Comunità", + "community_reqs": "minuscole, trattini bassi e nessuno spazio.", + "edit": "modifica", + "reply": "rispondi", + "cancel": "Annulla", + "preview": "Anteprima", + "upload_image": "carica immagine", + "formatting_help": "aiuto formattazione", + "view_source": "visualizza sorgente", + "unlock": "sblocca", + "lock": "blocca", + "sticky": "evidenzia", + "unsticky": "rimuovi evidenza", + "link": "link", + "mod": "moderatore", + "mods": "moderatori", + "moderates": "Moderatore di", + "settings": "Impostazioni", + "remove_as_mod": "rimuovi come moderatore", + "appoint_as_mod": "nomina come moderatore", + "modlog": "Registro di moderazione", + "admin": "amministratore", + "admins": "amministratori", + "remove_as_admin": "rimuovi come amministratore", + "appoint_as_admin": "nomina come amministratore", + "remove": "rimuovi", + "removed": "rimosso", + "locked": "bloccato", + "stickied": "evidenziato", + "reason": "Ragione", + "mark_as_read": "segna come letto", + "mark_as_unread": "segna come non letto", + "delete": "cancella", + "deleted": "cancellato", + "delete_account": "Cancella Account", + "delete_account_confirm": "Attenzione: stai per cancellare permanentemente tutti i tuoi dati. Sei sicuro?", + "restore": "ripristina", + "ban": "ban", + "ban_from_site": "banna dal sito", + "unban": "rimuovi ban", + "unban_from_site": "rimuove il ban dal sito", + "banned": "bannato", + "save": "salva", + "unsave": "rimuovi", + "create": "crea", + "creator": "autore", + "username": "Username", + "email_or_username": "Email o Username", + "number_of_users": "{{count}} Utenti", + "number_of_subscribers": "{{count}} Iscritti", + "number_of_points": "{{count}} Punti", + "number_online": "{{count}} Utenti Online", + "name": "Nome", + "title": "Titolo", + "category": "Categoria", + "subscribers": "Iscritti", + "both": "Entrambi", + "saved": "Salvato", + "unsubscribe": "Disiscriviti", + "subscribe": "Iscriviti", + "subscribed": "Iscritto", + "prev": "Precedente", + "next": "Prossima", + "sidebar": "Barra laterale", + "sort_type": "Ordina per", + "hot": "Popolari", + "new": "Nuovi", + "top_day": "Migliori della giornata", + "week": "Settimana", + "month": "Mese", + "year": "Anno", + "all": "Tutti", + "top": "Migliori", + "api": "API", + "inbox": "Posta in arrivo", + "inbox_for": "Posta di <1>{{user}}", + "mark_all_as_read": "segna tutti come letti", + "type": "Tipo", + "unread": "Non letti", + "replies": "Risposte", + "mentions": "Menzioni", + "reply_sent": "Risposta inviata", + "search": "Cerca", + "overview": "Panoramica", + "view": "Visualizza", + "logout": "Logout", + "login_sign_up": "Login / Iscriviti", + "login": "Login", + "sign_up": "Iscriviti", + "notifications_error": "Le notifiche desktop non sono supportate sul tuo browser. Prova Firefox o Chrome.", + "unread_messages": "Messaggi Non Letti", + "password": "Password", + "verify_password": "Verifica Password", + "email": "Email", + "optional": "Opzionale", + "expires": "Scade", + "url": "URL", + "body": "Contenuto", + "copy_suggested_title": "copia titolo suggerito: {{title}}", + "community": "Comunità", + "expand_here": "Visualizza qui", + "subscribe_to_communities": "Iscriviti ad una <1>comunità.", + "chat": "Chat", + "recent_comments": "Commenti Recenti", + "no_results": "Nessun risultato.", + "setup": "Setup", + "lemmy_instance_setup": "Setup dell'istanza di Lemmy", + "setup_admin": "Imposta Amministratore del Sito", + "your_site": "il tuo sito", + "modified": "modificato", + "nsfw": "NSFW", + "show_nsfw": "Mostra contenuto NSFW", + "theme": "Tema", + "sponsors": "Sponsors", + "sponsors_of_lemmy": "Sponsors di Lemmy", + "sponsor_message": "Lemmy è un software gratuito e <1>open-source, il che significa nessuna pubblicità, monetizzazione o investitori esterni, per sempre. Le tue donazioni supportano direttamente lo sviluppo full-time del progetto. Si ringraziano le seguenti persone:", + "support_on_patreon": "Supporta su Patreon", + "support_on_liberapay": "Supporta su Liberapay", + "general_sponsors": "I \"General Sponsors\" sono quelli che hanno investito dai 10$ ai 39$ su Lemmy.", + "crypto": "Crypto", + "bitcoin": "Bitcoin", + "ethereum": "Ethereum", + "monero": "Monero", + "code": "Code", + "joined": "Iscritto da", + "by": "di", + "to": "su", + "transfer_community": "trasferisci comunità", + "transfer_site": "trasferisci sito", + "are_you_sure": "sei sicuro?", + "yes": "si", + "no": "no", + "powered_by": "Powered by", + "landing_0": "Lemmy è un <1>aggregatore di link / alternativa a reddit, creato per integrarsi con il <2>fediverse. <3>È self-hosted, i commenti sono aggiornati in tempo reale ed è molto piccolo (<4>~80kB). La Federazione con la rete ActivityPub sarà implementata nel futuro. <5>Questa versione è una <6>beta molto giovane e molte funzionalità sono incomplete o mancanti. <7>Suggerisci nuove funzionalità o segnala errori a <8>questa pagina.<9>Sviluppato con <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.", + "not_logged_in": "Non hai effettuato l'accesso.", + "community_ban": "Sei stato bannato da questa comunità.", + "site_ban": "Sei stato bannato dal sito", + "couldnt_create_comment": "Impossibile creare il commento.", + "couldnt_like_comment": "Impossibile mettere 'Mi piace' al commento.", + "couldnt_update_comment": "Impossibile aggiornare il commento.", + "couldnt_save_comment": "Impossibile salvare il commento.", + "no_comment_edit_allowed": "Non sei autorizzato a modificare il commento.", + "no_post_edit_allowed": "Non sei autorizzato a modificare il post.", + "no_community_edit_allowed": "Non sei autorizzato a modificare la comunità.", + "couldnt_find_community": "Impossibile trovare la comunità.", + "couldnt_update_community": "Impossibile aggiornare la comunità.", + "community_already_exists": "La comunità esiste già.", + "community_moderator_already_exists": "Questo utente è già moderatore della comunità.", + "community_follower_already_exists": "Questo utente è già moderatore della comunità.", + "community_user_already_banned": "L'utente della comunità è già stato bannato.", + "couldnt_create_post": "Impossibile creare il post.", + "couldnt_like_post": "Impossibile mettere 'Mi piace' post.", + "couldnt_find_post": "Impossibile trovare il post.", + "couldnt_get_posts": "Impossibile recuperare i post", + "couldnt_update_post": "Impossibile aggiornare il post", + "couldnt_save_post": "Impossibile salvare il post.", + "no_slurs": "Niente offese.", + "not_an_admin": "Non un amministratore.", + "site_already_exists": "Il sito esiste già.", + "couldnt_update_site": "Impossibile aggiornare il sito.", + "couldnt_find_that_username_or_email": "L'username o la email non sono stati trovati.", + "password_incorrect": "Password non corretta.", + "passwords_dont_match": "Le password non corrispondono.", + "admin_already_created": "Spiacente, esiste già un amministratore.", + "user_already_exists": "L'utente esiste già.", + "couldnt_update_user": "Impossibile aggiornare l'utente.", + "system_err_login": "Si è verificato un errore. Prova ad effettuare nuovamente il login." +} diff --git a/ui/assets/translations/nl.json b/ui/assets/translations/nl.json new file mode 100644 index 000000000..d83128e9c --- /dev/null +++ b/ui/assets/translations/nl.json @@ -0,0 +1,232 @@ +{ + "post": "post", + "remove_post": "Verwijder post", + "no_posts": "Geen posts.", + "create_a_post": "Plaats een post", + "create_post": "Plaats post", + "number_of_posts": "{{count}} posts", + "posts": "posts", + "related_posts": "Deze posts kunnen gerelateerd zijn", + "cross_posts": "Deze link is ook geplaatst in:", + "cross_post": "cross-post", + "comments": "Reacties", + "number_of_comments": "{{count}} reacties", + "remove_comment": "Verwijder reactie", + "communities": "Communities", + "users": "Gebruikers", + "create_a_community": "Maak een community", + "create_community": "Maak community", + "remove_community": "Verwijder community", + "subscribed_to_communities": "Geabonneerd op <1>communities", + "trending_communities": "Populaire <1>communities", + "list_of_communities": "Lijst van communities", + "number_of_communities": "{{count}} communities", + "community_reqs": "kleine letters, onderstrepingsteken en geen spaties", + "edit": "bewerk", + "reply": "reageer", + "cancel": "Annuleer", + "unlock": "ontsluiten", + "lock": "sluiten", + "link": "link", + "mod": "moderator", + "mods": "moderators", + "moderates": "Modereert", + "settings": "Instellingen", + "remove_as_mod": "Verwijder als moderator", + "appoint_as_mod": "Benoemen tot moderator", + "modlog": "Moderatorlog", + "admin": "beheerder", + "admins": "beheerders", + "remove_as_admin": "verwijder als beheerder", + "appoint_as_admin": "benoemen tot beheerder", + "remove": "weghalen", + "removed": "weggehaald", + "locked": "gesloten", + "reason": "Reden", + "mark_as_read": "markeer als gelezen", + "mark_as_unread": "markeer als ongelezen", + "delete": "verwijder", + "deleted": "verwijderd", + "restore": "herstellen", + "ban": "verban", + "ban_from_site": "verban van site", + "unban": "verbanning opzeggen", + "unban_from_site": "verbanning van site opzeggen", + "save": "opslaan", + "unsave": "unsave", + "create": "maak", + "username": "Gebruikersnaam", + "email_or_username": "E-mail of gebruikersnaam", + "number_of_users": "{{count}} gebruikers", + "number_of_subscribers": "{{count}} abonnees", + "number_of_points": "{{count}} punten", + "name": "Naam", + "title": "Titel", + "category": "Categorie", + "subscribers": "Abonnees", + "both": "Beide", + "saved": "Opgeslagen", + "unsubscribe": "Afmelden", + "subscribe": "Abonneren", + "subscribed": "Geabonneerd", + "prev": "Vorige", + "next": "Volgende", + "sidebar": "Zijbalk", + "sort_type": "Sorteertype", + "hot": "Populair", + "new": "Nieuw", + "top_day": "Dagelijkse top", + "week": "Week", + "month": "Maand", + "year": "Jaar", + "all": "Alle", + "top": "Top", + "api": "API", + "inbox": "Postvak-in", + "inbox_for": "Postvak-in voor <1>{{user}}", + "mark_all_as_read": "markeer alle als gelezen", + "type": "Type", + "unread": "Ongelezen", + "reply_sent": "Reactie gestuurd", + "search": "Zoek", + "overview": "Overzicht", + "view": "Beeld", + "logout": "Log uit", + "login_sign_up": "Log in / Aanmelden", + "login": "Log in", + "sign_up": "Aanmelden", + "notifications_error": + "Bureabladberichten niet beschikbaar in je browser. Probeer Firefox of Chrome.", + "unread_messages": "Ongelezen berichten", + "password": "Wachtwoord", + "verify_password": "Herhaal wachtwoord", + "email": "E-mail", + "optional": "Optioneel", + "expires": "Verloopt", + "url": "url", + "body": "Tekst", + "copy_suggested_title": "neem voorgestelde titel over: {{title}}", + "community": "Community", + "expand_here": "Breid hier uit", + "subscribe_to_communities": "Abonneer je op een paar <1>communities.", + "chat": "Praat", + "recent_comments": "Recente reacties", + "no_results": "Geen resultaten", + "setup": "Installatie", + "lemmy_instance_setup": "Installatie van Lemmy-instantie", + "setup_admin": "Maak een administrator", + "your_site": "jouw site", + "modified": "bewerkt", + "nsfw": "NSFW", + "show_nsfw": "Laat NSFW-inhoud zien", + "sponsors": "Sponsoren", + "sponsors_of_lemmy": "Sponsoren van Lemmy", + "sponsor_message": + "Lemmy is vrije, <1>open-source software, dus zonder reclame, winstoogmerk en durfkapitaal, punt. Jouw donaties gaan direct naar de full-time-ontwikkeling van het project. Met veel dank aan de volgende mensen:", + "support_on_patreon": "Ondersteun op Patreon", + "support_on_liberapay": "Ondersteun op Liberapay", + "general_sponsors": + "Algemene sponsors zijn sponsors die tussen de $10 en $39 hebben gegeven aan Lemmy.", + "crypto": "Cryptovaluta", + "bitcoin": "Bitcoin", + "ethereum": "Ethereum", + "monero": "Monero", + "code": "Code", + "joined": "toegetreden", + "by": "door", + "to": "aan", + "transfer_community": "community overplaatsen", + "transfer_site": "site overplaatsen", + "are_you_sure": "weet je het zeker?", + "yes": "ja", + "no": "nee", + "powered_by": "Mogelijk gemaakt door", + "landing_0": + "Lemmy is een <1>linkverzameler / reddit-alternatief, bedoeld om in de <2>fediverse te werken.<3>Lemmy kan door om het even wie gehost worden, heeft live-bijgewerkte reacties en is superklein (<4>ca. 80 kB). Federatie in hte ActivityPub-netwerk is gepland. <5>Dit is een <6>erg vroege bèta-versie, en een hoop functies zijn stuk of afwezig. <7>Stel nieuwe functies voor of meldt fouten <8>hier.<9>Gemaakt met <10>Rust, <11>Actix, <12>Inferno en <13>Typescript.", + "not_logged_in": "Niet ingelogd.", + "community_ban": "Je bent verbannen uit deze community.", + "site_ban": "Je bent verbannen van deze site.", + "couldnt_create_comment": "Kon reactie niet maken.", + "couldnt_like_comment": "Kon reactie niet leuk vinden.", + "couldnt_update_comment": "Kon reactie niet bijwerken.", + "couldnt_save_comment": "Kon reactie niet opslaan.", + "no_comment_edit_allowed": "Niet toegestaan om reactie te bewerken.", + "no_post_edit_allowed": "Niet toegestaan om posts te bewerken.", + "no_community_edit_allowed": "Niet toegestaan om community te bewerken.", + "couldnt_find_community": "Kon community niet vinden.", + "couldnt_update_community": "Kon community niet bijwerken.", + "community_already_exists": "Community bestaat al.", + "community_moderator_already_exists": "Community-moderator bestaat al.", + "community_follower_already_exists": "Community-volger bestaat al.", + "community_user_already_banned": "Community-gebruiker reeds verbannen.", + "couldnt_create_post": "Kon post niet maken.", + "couldnt_like_post": "Kon post niet leuk vinden.", + "couldnt_find_post": "Kon post niet vinden.", + "couldnt_get_posts": "Kon posts niet ophalen.", + "couldnt_update_post": "Kon post niet bijwerken.", + "couldnt_save_post": "Kon post niet opslaan.", + "no_slurs": "Geen beledigingen.", + "not_an_admin": "Niet een beheerder.", + "site_already_exists": "Site bestaat al.", + "couldnt_update_site": "Kon site niet bijwerken.", + "couldnt_find_that_username_or_email": + "Kon gebruikersnaam of e-mailadres niet vinden.", + "password_incorrect": "Wachtwoord incorrect.", + "passwords_dont_match": "Wachtwoorden zijn niet gelijk.", + "admin_already_created": "Sorry, er is al een beheerder.", + "user_already_exists": "Gebruiker bestaat al.", + "couldnt_update_user": "Kon gebruiker niet bijwerken.", + "system_err_login": + "Systeemfout. Probeer uit te loggen en weer in te loggen.", + "preview": "voorbeeld", + "upload_image": "Afbeelding uploaden", + "avatar": "Avatar", + "upload_avatar": "Avatar uploaden", + "show_avatars": "Toon avatars", + "formatting_help": "Opmaak hulp", + "view_source": "bekijk bron", + "sticky": "vastplakken", + "unsticky": "loshalen", + "archive_link": "Archiveer link", + "stickied": "vastgeplakt", + "delete_account": "Verwijder account", + "delete_account_confirm": "Waarschuwing: dit zal al uw data voorgoed verwijderen, vul uw wachtwoord in om te bevestigen.", + "banned": "verbannen", + "creator": "auteur", + "number_online": "{{count}} gebruikers online", + "docs": "Documentatie", + "replies": "Reacties", + "mentions": "vermeldingen", + "old_password": "Oud wachtwoord", + "forgot_password": "wachtwoord vergeten", + "reset_password_mail_sent": "Stuur een email om uw wachtwoord te resetten", + "password_change": "Wachtwoord aanpassen", + "new_password": "Nieuw wachtwoord", + "no_email_setup": "Deze server heeft email niet correct opgezet", + "send_notifications_to_email": "Stuur meldingen naar je email", + "language": "Taal", + "browser_default": "Browser standaard", + "downvotes_disabled": "Downvotes geblokkeerd", + "enable_downvotes": "Downvotes toestaan", + "open_registration": "Open registratie", + "registration_closed": "Registratie gesloten", + "enable_nsfw": "NSFW toestaan", + "theme": "Thema", + "create_private_message": "Maak een beveiligd bericht", + "send_secure_message": "Verstuur beveiligd bericht", + "send_message": "Verstuur bericht", + "message": "Bericht", + "old": "Oud", + "message_sent": "Bericht verstuurd", + "messages": "Berichten", + "matrix_user_id": "Matrix gebruikers-id", + "private_message_disclaimer": "Waarschuwing: Privé berichten in Lemmy zijn niet beveiligd. Maak een account aan op <1>Riot.im om veilig te communiceren", + "donate_to_lemmy": "Doneer aan Lemmy", + "donate": "Doneer", + "from": "van", + "logged_in": "Ingelogd", + "email_already_exists": "Email bestaat al", + "couldnt_create_private_message": "Kan beveiligd bericht niet maken", + "no_private_message_edit_allowed": "Niet toegestaan om privé berichten te wijzigen", + "couldnt_update_private_message": "Kan beveiligd bericht niet bijwerken" +} diff --git a/ui/assets/translations/pt_br.json b/ui/assets/translations/pt_br.json new file mode 100644 index 000000000..732ab6325 --- /dev/null +++ b/ui/assets/translations/pt_br.json @@ -0,0 +1,239 @@ +{ + "post": "publicação", + "remove_post": "Apagar publicação", + "no_posts": "Sem publicações.", + "create_a_post": "Criar uma publicação", + "create_post": "Criar publicação", + "number_of_posts": "{{count}} publicações", + "posts": "Publicações", + "related_posts": "Essas publicações podem estar relacionadas", + "cross_posts": "Esse link também foi publicado em:", + "cross_post": "re-publicar", + "cross_posted_to": "Publicado também em: ", + "comments": "Comentários", + "number_of_comments": "{{count}} comentários", + "remove_comment": "Apagar comentário", + "communities": "Comunidades", + "users": "Usuários", + "create_a_community": "Criar uma comunidade", + "create_community": "Criar comunidade", + "remove_community": "Apagar comunidade", + "subscribed_to_communities": "Inscrito em <1>comunidades", + "trending_communities": "<1>Comunidades em tendência", + "list_of_communities": "Lista de comunidades", + "number_of_communities": "{{count}} comunidades", + "community_reqs": "minúsculas, sublinhados e sem espaços.", + "create_private_message": "Criar mensagem privada", + "send_secure_message": "Enviar mensagem segura", + "send_message": "Enviar mensagem", + "message": "Mensagem", + "edit": "editar", + "reply": "responder", + "cancel": "Cancelar", + "preview": "Pré-visualização", + "upload_image": "fazer upload de imagem", + "avatar": "Avatar", + "upload_avatar": "Fazer upload de avatar", + "show_avatars": "Mostrar Avatars", + "formatting_help": "ajuda de formatação", + "view_source": "ver fonte", + "unlock": "desbloquear", + "lock": "bloquear", + "sticky": "fixar", + "unsticky": "desafixar", + "link": "link", + "archive_link": "arquivar link", + "mod": "moderador", + "mods": "moderadores", + "moderates": "Modera", + "settings": "Configurações", + "remove_as_mod": "remover como moderador", + "appoint_as_mod": "designar como moderador", + "modlog": "Registro de moderação", + "admin": "administrador", + "admins": "administradores", + "remove_as_admin": "remover como administrador", + "appoint_as_admin": "designar como administrador", + "remove": "remover", + "removed": "removido", + "locked": "trancado", + "stickied": "fixado", + "reason": "Motivo", + "mark_as_read": "marcar como lido", + "mark_as_unread": "marcar como não lido", + "delete": "apagar", + "deleted": "apagado", + "delete_account": "Apagar conta", + "delete_account_confirm": + "Aviso: isso vai apagar seus dados de forma permanente. Escreva sua senha para confirmar.", + "restore": "restaurar", + "ban": "banir", + "ban_from_site": "banido do site", + "unban": "readmitido", + "unban_from_site": "readmitido ao site", + "banned": "banido", + "save": "guardar", + "unsave": "descartar", + "create": "criar", + "creator": "criador", + "username": "nome de usuário", + "email_or_username": "E-mail ou nome de usuário", + "number_of_users": "{{count}} usuários", + "number_of_subscribers": "{{count}} inscritos", + "number_of_points": "{{count}} pontos", + "number_online": "{{count}} usuários online", + "name": "Nome", + "title": "Título", + "category": "Categoria", + "subscribers": "Inscritos", + "both": "Ambos", + "saved": "Guardado", + "unsubscribe": "Cancelar inscrição", + "subscribe": "Inscrever-se", + "subscribed": "Inscrito", + "prev": "Anterior", + "next": "Próximo", + "sidebar": "Barra lateral", + "sort_type": "Ordenação", + "hot": "Popular", + "new": "Novo", + "old": "Velho", + "top_day": "Top do dia", + "week": "Semana", + "month": "Mês", + "year": "Ano", + "all": "Tudo", + "top": "Top", + "api": "API", + "docs": "Docs", + "inbox": "Caixa de entrada", + "inbox_for": "Caixa de entrada de <1>{{user}}", + "mark_all_as_read": "marcar tudo como lido", + "type": "Tipo", + "unread": "Não lido", + "replies": "Respostas", + "mentions": "Menções", + "reply_sent": "Resposta enviada", + "message_sent": "Mensagem enviada", + "search": "Busca", + "overview": "Visão geral", + "view": "Visualização", + "logout": "Sair", + "login_sign_up": "Entrar / Inscrever-se", + "login": "Entrar", + "sign_up": "Inscrever-se", + "notifications_error": + "Seu navegador não oferece notificações para a área de trabalho. Tente o Firefox ou o Chrome.", + "unread_messages": "Mensagens não lidas", + "messages": "Mensagens", + "password": "Senha", + "verify_password": "Verifique a senha", + "old_password": "Senha antiga", + "forgot_password": "esqueci a senha", + "reset_password_mail_sent": "Enviado um e-mail para a alteração da senha.", + "password_change": "Alteração de senha", + "new_password": "Nova senha", + "no_email_setup": "Esse servidor não configurou corretamente o e-mail.", + "email": "E-mail", + "matrix_user_id": "Usuário Matrix", + "private_message_disclaimer": + "Aviso: mensagens privadas no Lemmy não são seguras. Crie uma conta em <1>Riot.im para troca segura de mensagens.", + "send_notifications_to_email": "Enviar notificações para o e-mail", + "optional": "Opcional", + "expires": "Expira", + "language": "Idioma", + "browser_default": "Padrão do navegador", + "downvotes_disabled": "Votos negativos desativados", + "enable_downvotes": "Permitir votos negativos", + "open_registration": "Permitir registro", + "registration_closed": "Registros desativados", + "enable_nsfw": "Permitir NSFW", + "url": "URL", + "body": "Conteúdo", + "copy_suggested_title": "copiar título sugerido: {{title}}", + "community": "Comunidade", + "expand_here": "Expandir aqui", + "subscribe_to_communities": "Inscreva-se em algumas <1>comunidades.", + "chat": "Chat", + "recent_comments": "Últimos comentários", + "no_results": "Nenhum resultado.", + "setup": "Instalação", + "lemmy_instance_setup": "Criação de instância Lemmy", + "setup_admin": "Configurar administrador do site", + "your_site": "seu site", + "modified": "modificado", + "nsfw": "NSFW", + "show_nsfw": "Mostrar conteúdo NSFW", + "theme": "Tema", + "sponsors": "Patrocinadores", + "sponsors_of_lemmy": "Patrocinadores do Lemmy", + "sponsor_message": + "Lemmy é um programa livre e de código aberto, o que significa que não haverá publicidade, monetização ou capital de risco, jamais. Suas doações apoiam de forma direta o desenvolvimento em tempo integral do projeto. Muitos agradecimentos às sequintes pessoas:", + "support_on_patreon": "Colabore no Patreon", + "support_on_liberapay": "Colabore no Liberapay", + "donate_to_lemmy": "Faça uma doação ao Lemmy", + "donate": "Doar", + "general_sponsors": + "Patrocinadores são aqueles que doaram entre $10 e $39 ao Lemmy.", + "crypto": "Crypto", + "bitcoin": "Bitcoin", + "ethereum": "Ethereum", + "monero": "Monero", + "code": "Code", + "joined": "Entrou", + "by": "por", + "to": "para", + "from": "de", + "transfer_community": "transferir comunidade", + "transfer_site": "transferir site", + "are_you_sure": "tem certeza?", + "yes": "sim", + "no": "não", + "powered_by": "Powered by", + "landing_0": + "Lemmy é um <1>agregador de links / alternativa ao reddit, com a intenção de funcionar junto ao <2>fediverso.<3>Pode ser hospedado em servidor próprio, tem atualização de comentários em tempo real e é minúsculo (<4>~80kB). A federação com a rede ActivityPub está no roteiro do projeto. <5>Esta é uma <6>versão beta bastante antecipada, e muitas funcionalidades ainda estão quebradas ou ausentes. <7>Sugira novas funcionalidades ou reporte erros <8>aqui.<9>Feito com <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.", + "not_logged_in": "Não autenticado.", + "logged_in": "Autenticado.", + "community_ban": "Você foi banido desta comunidade.", + "site_ban": "Você foi banido do site", + "couldnt_create_comment": "Não foi possível criar o comentário.", + "couldnt_like_comment": "Não foi possível curtir o comentário.", + "couldnt_update_comment": "Não foi possível atualizar o comentário.", + "couldnt_save_comment": "Não foi possível guardar o comentário.", + "no_comment_edit_allowed": "Sem permissão para editar de comentário.", + "no_post_edit_allowed": "Sem permissão para editar publicação.", + "no_community_edit_allowed": "Sem permissão para editar comunidade.", + "couldnt_find_community": "Não foi possível encontrar a comunidade.", + "couldnt_update_community": "Não foi possível atualizar a comunidade.", + "community_already_exists": "Esta comunidade já existe.", + "community_moderator_already_exists": + "Este moderador da comunidade já existe.", + "community_follower_already_exists": "Este seguidor da comunidade já existe.", + "community_user_already_banned": "Este usuário da comunidade já foi banido.", + "couldnt_create_post": "Não foi possível criar a publicação.", + "couldnt_like_post": "Não foi possível curtir a publicação.", + "couldnt_find_post": "Não foi possível encontrar a publicação.", + "couldnt_get_posts": "Não foi possível obter as publicações", + "couldnt_update_post": "Não foi possível atualizar a publicação", + "couldnt_save_post": "Não foi possível guardar a publicação.", + "no_slurs": "Sem insultos.", + "not_an_admin": "Não é administrador.", + "site_already_exists": "O site já existe.", + "couldnt_update_site": "Não foi possível atualizar o site.", + "couldnt_find_that_username_or_email": + "Não foi possível encontrar esse usuário ou e-mail.", + "password_incorrect": "Senha incorreta.", + "passwords_dont_match": "As senhas não são iguais.", + "admin_already_created": "Desculpe, já há um administrador.", + "user_already_exists": "Este usuário já existe.", + "email_already_exists": "Este e-mail já existe.", + "couldnt_update_user": "Não foi possível atualizar o usuário.", + "system_err_login": "Erro no sistema. Tente sair e autenticar-se outra vez.", + "couldnt_create_private_message": "Não foi possível criar mensagem privada.", + "no_private_message_edit_allowed": + "Sem permissão para editar mensagem privada.", + "couldnt_update_private_message": + "Não foi possível atualizar a mensagem privada.", + "time": "Tempo", + "action": "Ação" +} diff --git a/ui/assets/translations/ru.json b/ui/assets/translations/ru.json new file mode 100644 index 000000000..4d708173e --- /dev/null +++ b/ui/assets/translations/ru.json @@ -0,0 +1,168 @@ +{ + "post": "запись", + "remove_post": "Удалить запись", + "no_posts": "Нет записей.", + "create_a_post": "Создать запись", + "create_post": "Создать запись", + "number_of_posts": "{{count}} записей", + "posts": "Записи", + "related_posts": "Эти записи могут быть связаны", + "comments": "Комментарии", + "number_of_comments": "{{count}} комментариев", + "remove_comment": "Удалить комментарий", + "communities": "Сообщества", + "users": "Пользователи", + "create_a_community": "Создать сообщество", + "create_community": "Создать сообщество", + "remove_community": "Удалить сообщество", + "subscribed_to_communities": "Подписаны на <1>сообщества", + "trending_communities": "<1>Сообщества в тренде", + "list_of_communities": "Список сообществ", + "community_reqs": "строчными буквами, подчеркиваниями и без пробелов.", + "edit": "редактировать", + "reply": "ответить", + "cancel": "Отмена", + "unlock": "разблокировать", + "lock": "заблокировать", + "link": "ссылка", + "mod": "модератор", + "mods": "модераторы", + "moderates": "Модерация", + "settings": "Настройки", + "remove_as_mod": "снять из модераторов", + "appoint_as_mod": "назначить модератором", + "modlog": "Модлог", + "admin": "администратор", + "admins": "администраторы", + "remove_as_admin": "снять из администраторов", + "appoint_as_admin": "назначить администратором", + "remove": "убрать", + "removed": "убрано", + "locked": "заблокировано", + "reason": "Причина", + "mark_as_read": "пометить как прочитанное", + "mark_as_unread": "пометить как непрочитанное", + "delete": "удалить", + "deleted": "удалено", + "restore": "восстановить", + "ban": "заблокировать", + "ban_from_site": "заблокировать на сайте", + "unban": "разблокировать", + "unban_from_site": "разблокировать на сайте", + "save": "сохранить", + "unsave": "не сохранять", + "create": "создать", + "username": "Имя пользователя", + "email_or_username": "Электронная почта или имя пользователя", + "number_of_users": "{{count}} пользователей", + "number_of_subscribers": "{{count}} подписчиков", + "number_of_points": "{{count}} баллов", + "name": "Имя", + "title": "Название", + "category": "Категория", + "subscribers": "Подписчики", + "both": "Оба", + "saved": "Сохранено", + "unsubscribe": "Отписаться", + "subscribe": "Подписаться", + "subscribed": "Подписаны", + "prev": "Назад", + "next": "Далее", + "sidebar": "Боковая панель", + "sort_type": "Тип сортировки", + "hot": "Популярно", + "new": "Новое", + "top_day": "Лучшее за день", + "week": "Неделя", + "month": "Месяц", + "year": "Год", + "all": "Всё", + "top": "Лучшее", + "api": "API", + "inbox": "Входящие", + "inbox_for": "Входящие сообщения для <1>{{user}}", + "mark_all_as_read": "пометить все как прочитанные", + "type": "Тип", + "unread": "Не прочитано", + "reply_sent": "Ответ отправлен", + "search": "Поиск", + "overview": "Обзор", + "view": "Просмотр", + "logout": "Выйти", + "login_sign_up": "Войти / Регистрация", + "login": "Авторизация", + "sign_up": "Регистрация", + "notifications_error": + "Уведомления на рабочем столе недоступны в вашем браузере. Попробуйте Firefox или Chrome.", + "unread_messages": "Непрочитанные сообщения", + "password": "Пароль", + "verify_password": "Повторите пароль", + "email": "Электронная почта", + "optional": "Необязательно", + "expires": "Истёк", + "url": "URL", + "body": "Тело", + "copy_suggested_title": "предложенное название: {{title}}", + "community": "Сообщество", + "expand_here": "Расширить здесь", + "subscribe_to_communities": "Подпишитесь на некоторые <1>сообщества.", + "chat": "Чат", + "no_results": "Нет результатов.", + "setup": "Установка", + "lemmy_instance_setup": "Установка инстанции Lemmy", + "setup_admin": "Настройка администратора сайта", + "your_site": "ваш сайт", + "modified": "изменено", + "nsfw": "NSFW", + "show_nsfw": "Показывать NSFW-контент", + "sponsors": "Спонсоры", + "sponsors_of_lemmy": "Спонсоры Lemmy", + "sponsor_message": + "Lemmy это бесплатное, <1>открытое программное обеспечение, что означает отсутствие рекламы, монетизации или венчурного капитала, никогда. Ваши пожертвования напрямую поддерживают развитие проекта. Спасибо нижеуказанным людям:", + "support_on_patreon": "Поддержать на Patreon", + "general_sponsors": + "Генеральные спонсоры - это те, кто пообещал Lemmy от $10 до $39.", + "crypto": "Крипто", + "bitcoin": "Bitcoin", + "ethereum": "Ethereum", + "code": "Код", + "joined": "Присоединился", + "powered_by": "Работает на", + "landing_0": + "Lemmy - это <1>агрегатор ссылок / альтернатива reddit, предназначенный для работы в <2>федиверсе.<3>Это самодостаточная система, с обновляемыми комментариями, и эта система крошечная (<4>~80 Кб). Федерация в сети ActivityPub находится в разработке. <5>Это <6>очень ранняя бета-версия, и многие функции в настоящее время сломаны или отсутствуют. <7>Предлагать новые функции или сообщать об ошибках можно <8>здесь.<9>Сделано на <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.", + "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": "Сообщество уже существует.", + "community_moderator_already_exists": "Модератор сообщества уже существует.", + "community_follower_already_exists": "Подписчик сообщества уже существует.", + "community_user_already_banned": "Пользователь сообщества уже заблокирован.", + "couldnt_create_post": "Не получилось создать запись.", + "couldnt_like_post": "Не получилось лайкнуть запись.", + "couldnt_find_post": "Не получилось найти запись.", + "couldnt_get_posts": "Не получилось найти записи", + "couldnt_update_post": "Не получилось обновить запись", + "couldnt_save_post": "Не получилось сохранить запись.", + "no_slurs": "Без оскорблений.", + "not_an_admin": "Не администратор.", + "site_already_exists": "Сайт уже существует.", + "couldnt_update_site": "Не получилось обновить сайт.", + "couldnt_find_that_username_or_email": + "Не получилось найти данное имя пользователя или электронную почту.", + "password_incorrect": "Неверный пароль.", + "passwords_dont_match": "Пароли не совпадают.", + "admin_already_created": "Извините, уже есть администратор.", + "user_already_exists": "Пользователь уже существует.", + "couldnt_update_user": "Не получилось обновить пользователя.", + "system_err_login": + "Системная ошибка. Попробуйте выйти из системы и вернуться обратно." +} diff --git a/ui/assets/translations/sv.json b/ui/assets/translations/sv.json new file mode 100644 index 000000000..5457409ca --- /dev/null +++ b/ui/assets/translations/sv.json @@ -0,0 +1,193 @@ +{ + "post": "inlägg", + "remove_post": "Radera inlägg", + "no_posts": "Inga inlägg.", + "create_a_post": "Skriv ett inlägg", + "create_post": "Skapa inlägg", + "number_of_posts": "{{count}} inlägg", + "posts": "Inlägg", + "related_posts": "Dessa inlägg kan vara relaterade", + "cross_posts": "Den här länken har även publicerats i:", + "cross_post": "tvärinlägg", + "comments": "Kommentarer", + "number_of_comments": "{{count}} kommentarer", + "remove_comment": "Radera kommentar", + "communities": "Gemenskaper", + "users": "Användare", + "create_a_community": "Skapa en gemenskap", + "create_community": "Skapa gemenskap", + "remove_community": "Radera gemenskap", + "subscribed_to_communities": "Prenumererar på <1>gemenskaper", + "trending_communities": "Populära <1>gemenskaper", + "list_of_communities": "Lista över gemenskaper", + "number_of_communities": "{{count}} gemenskaper", + "community_reqs": "gemener, understreck och inga blanksteg.", + "edit": "redigera", + "reply": "svara", + "cancel": "Avbryt", + "preview": "Förhandsgranskning", + "upload_image": "ladda upp bild", + "formatting_help": "formateringshjälp", + "view_source": "visa källkod", + "unlock": "lås upp", + "lock": "lås", + "sticky": "fastnålad", + "unsticky": "inte fastnålad", + "link": "länk", + "mod": "moderator", + "mods": "moderatorer", + "moderates": "Modererar", + "settings": "Inställningar", + "remove_as_mod": "tag bort som moderator", + "appoint_as_mod": "lägg till som moderator", + "modlog": "Moderationslogg", + "admin": "administratör", + "admins": "administratörer", + "remove_as_admin": "tag bort som administratör", + "appoint_as_admin": "lägg till som administratör", + "remove": "ta bort", + "removed": "borttagen", + "locked": "låst", + "stickied": "fastnålad", + "reason": "Anledning", + "mark_as_read": "markera som läst", + "mark_as_unread": "markera som oläst", + "delete": "radera", + "deleted": "raderad", + "delete_account": "Ta bort konto", + "delete_account_confirm": + "Varning: den här åtgärden kommer radera alla dina data permanent. Är du säker?", + "restore": "återställ", + "ban": "blockera", + "ban_from_site": "blockera från webbplats", + "unban": "ta bort blockering", + "unban_from_site": "ta bort blockering från webbplats", + "banned": "blocerad", + "save": "spara", + "unsave": "förkasta", + "create": "skapa", + "creator": "skapare", + "username": "Användarnamn", + "email_or_username": "E-postadress eller användarnamn", + "number_of_users": "{{count}} användare", + "number_of_subscribers": "{{count}} prenumeranter", + "number_of_points": "{{count}} poäng", + "number_online": "{{count}} användare inloggade", + "name": "Namn", + "title": "Titel", + "category": "Kategori", + "subscribers": "Prenumeranter", + "both": "Båda", + "saved": "Sparade", + "unsubscribe": "Avbryt prenumeration", + "subscribe": "Prenumerera", + "subscribed": "Prenumererar", + "prev": "Föregående", + "next": "Nästa", + "sidebar": "Sidlist", + "sort_type": "Sorteringstyp", + "hot": "Hett", + "new": "Nytt", + "top_day": "Dagstoppen", + "week": "Vecka", + "month": "Månad", + "year": "År", + "all": "Samtliga", + "top": "Topp", + "api": "API", + "inbox": "Inkorg", + "inbox_for": "Inkorg tillhörande <1>{{user}}", + "mark_all_as_read": "markera alla som lästa", + "type": "Typ", + "unread": "Oläst", + "reply_sent": "Svar skickat", + "search": "Sök", + "overview": "Översikt", + "view": "Vy", + "logout": "Logga ut", + "login_sign_up": "Logga in eller skapa konto", + "login": "Logga in", + "sign_up": "Skapa konto", + "notifications_error": + "Din webbläsare har inte stöd för skrivbordsaviseringar. Testa Firefox eller Chrome.", + "unread_messages": "Olästa meddelanden", + "password": "Lösenord", + "verify_password": "Bekräfta lösenord", + "email": "E-postadress", + "optional": "Valfritt", + "expires": "Går ut", + "url": "URL", + "body": "Text", + "copy_suggested_title": "kopiera föreslagen titel: {{title}}", + "community": "Gemenskap", + "expand_here": "Utvidga här", + "subscribe_to_communities": "Prenumerera på några <1>gemenskaper.", + "chat": "Chatta", + "recent_comments": "Senaste kommentarer", + "no_results": "Inga resultat.", + "setup": "Installering", + "lemmy_instance_setup": "Installering av Lemmy-instans", + "setup_admin": "Skapa en administratör", + "your_site": "din webbplats", + "modified": "ändrades", + "nsfw": "Känsligt eller oförbehållsamt innehåll", + "show_nsfw": "Visa känsligt eller oförbehållsamt innehåll", + "theme": "Utseende", + "sponsors": "Sponsorer", + "sponsors_of_lemmy": "Lemmys sponsorer", + "sponsor_message": + "Lemmy är fri mjukvara med <1>öppen källkod, vilket innebär att ingen reklam, vinstindrivning eller venturekapital förekommer, någonsin. Dina donationer går direkt till att stöda utvecklingen av projektet. Stort tack till följande personer:", + "support_on_patreon": "Stöd på Patreon", + "general_sponsors": + "Allmänna sponsorer är dem som givit mellan 10 och 39\u00a0dollar till Lemmy.", + "crypto": "Kryptovaluta", + "bitcoin": "Bitcoin", + "ethereum": "Ethereum", + "monero": "Monero", + "code": "Kod", + "joined": "Gick med", + "by": "av", + "to": "till", + "transfer_community": "flytta gemenskap", + "transfer_site": "flytta webbplats", + "are_you_sure": "är du säker?", + "yes": "ja", + "no": "nej", + "powered_by": "Drivs av", + "landing_0": + "Lemmy är en <1>länksamlare och alternativ till reddit, ämnad att fungera i <2>Fediversumet.<3>Lemmy kan drivas av vem som helst, har kommentarstrådar som updateras i realid och är mycket liten (<4>ca 80\u00a0kB). Federering med ActivityPub-nätverket är planerat. <5>Detta är en <6>väldigt tidig betaversion och många funktioner saknas därför eller är trasiga.<7>Föreslå nya funktioner eller anmäl buggar <8>här.<9>Skapad i <10>Rust, <11>Actix, <12>Inferno och <13>Typescript.", + "not_logged_in": "Inte inloggad.", + "community_ban": "Du har blockerats från den här gemenskapen.", + "site_ban": "Du har blockerats från webbplatsen.", + "couldnt_create_comment": "Kunde inte skapa kommentar.", + "couldnt_like_comment": "Kunde inte gilla kommentar.", + "couldnt_update_comment": "Kunde inte uppdatera kommentar.", + "couldnt_save_comment": "Kunde inte spara kommentar.", + "no_comment_edit_allowed": "Har inte behörighet att redigera komentar.", + "no_post_edit_allowed": "Har inte behörighet att redigera inlägg.", + "no_community_edit_allowed": "Har inte behörighet att redigera gemenskap.", + "couldnt_find_community": "Kunde inte hitta gemenskap.", + "couldnt_update_community": "Kunde inte uppdatera gemenskap.", + "community_already_exists": "Gemenskapen finns redan.", + "community_moderator_already_exists": "Gemenskapsmoderatorn finns redan.", + "community_follower_already_exists": "Gemenskapsföljaren finns redan.", + "community_user_already_banned": "Gemenskapsanvändaren redan blockerad.", + "couldnt_create_post": "Kunde inte skapa inlägg.", + "couldnt_like_post": "Kunde inte gilla inlägg.", + "couldnt_find_post": "Kunde inte hitta inlägg.", + "couldnt_get_posts": "Kunde inte hämta inlägg.", + "couldnt_update_post": "Kunde inte uppdatera inlägg.", + "couldnt_save_post": "Kunde inte spara inlägg.", + "no_slurs": "Inga förolämpningar.", + "not_an_admin": "Inte en administratör.", + "site_already_exists": "Webbplatsen finns redan.", + "couldnt_update_site": "Kunde inte uppdatera webbplats.", + "couldnt_find_that_username_or_email": + "Kunde inte hitta det användarnamnet eller e-postadressen.", + "password_incorrect": "Ogiltigt lösenord.", + "passwords_dont_match": "Lösenorden stämmer inte överens.", + "admin_already_created": "Beklagar, men det finns redan en administratör.", + "user_already_exists": "Användaren finns redan.", + "couldnt_update_user": "Kunde inte uppdatera användare.", + "system_err_login": "Systemfel. Försök att logga ut och sedan in igen." +} diff --git a/ui/assets/translations/zh.json b/ui/assets/translations/zh.json new file mode 100644 index 000000000..6cf8a6698 --- /dev/null +++ b/ui/assets/translations/zh.json @@ -0,0 +1,162 @@ +{ + "post": "帖子", + "remove_post": "移除帖子", + "no_posts": "没有帖子.", + "create_a_post": "创建新帖子", + "create_post": "创建帖子", + "number_of_posts": "{{count}} 帖子", + "posts": "帖子", + "related_posts": "相关的帖子", + "comments": "评论", + "number_of_comments": "{{count}} 评论", + "remove_comment": "移除评论", + "communities": "节点", + "create_a_community": "创建新节点", + "create_community": "创建节点", + "remove_community": "移除节点", + "subscribed_to_communities": "订阅新 <1>节点", + "trending_communities": "<1>节点趋势", + "list_of_communities": "节点列表", + "community_reqs": "包含小写与下划线且没有空格的字符串.", + "edit": "编辑", + "reply": "回应", + "cancel": "取消", + "unlock": "解锁", + "lock": "加锁", + "link": "链接", + "mod": "监管人", + "mods": "监管人", + "moderates": "监管", + "remove_as_mod": "添加监管人", + "appoint_as_mod": "移除监管人", + "modlog": "监管记录", + "admin": "管理权限", + "admins": "管理权限", + "remove_as_admin": "移除管理权限", + "appoint_as_admin": "添加管理权限", + "remove": "移除", + "removed": "已移除", + "locked": "已加锁", + "reason": "原因", + "mark_as_read": "标记未读", + "mark_as_unread": "标记已读", + "delete": "删除", + "deleted": "已删除", + "restore": "恢复", + "ban": "禁止", + "ban_from_site": "禁止此站点", + "unban": "取消", + "unban_from_site": "取消禁止", + "save": "保存", + "unsave": "取消保存", + "create": "创建", + "username": "用户名", + "email_or_username": "邮箱或用户名", + "number_of_users": "{{count}} 用户", + "number_of_subscribers": "{{count}} 订阅", + "number_of_points": "{{count}} 分", + "name": "名字", + "title": "标题", + "category": "分类", + "subscribers": "订阅", + "both": "全部", + "saved": "保存", + "unsubscribe": "取消订阅", + "subscribe": "订阅", + "subscribed": "已订阅", + "prev": "上一页", + "next": "下一页", + "sidebar": "侧边栏", + "sort_type": "排序方式", + "hot": "最热", + "new": "最新", + "top_day": "今日", + "week": "周", + "month": "月", + "year": "年", + "all": "所有", + "top": "最热", + "api": "应用程式介面", + "inbox": "收件箱", + "inbox_for": "<1>{{user}} 收件箱", + "mark_all_as_read": "标记所有已读", + "type": "类型", + "unread": "未读", + "reply_sent": "回复发送", + "search": "搜索", + "overview": "个人中心", + "view": "查看", + "logout": "注销", + "login_sign_up": "登录/注册", + "login": "登录", + "sign_up": "注册", + "notifications_error": "你的浏览器不支持桌面通知,尝试 Firefox 或 Chrome", + "unread_messages": "未读消息", + "password": "密码", + "verify_password": "确认密码", + "email": "邮箱", + "optional": "选项", + "expires": "过期", + "url": "网址", + "body": "内容", + "copy_suggested_title": "复制建议的标题: {{title}}", + "community": "节点", + "expand_here": "展开", + "subscribe_to_communities": "订阅一些 <1>节点.", + "chat": "聊天", + "no_results": "没有结果.", + "setup": "设置", + "lemmy_instance_setup": "Lemmy Instance Setup", + "setup_admin": "设置管理员", + "your_site": "你的站点", + "modified": "修改", + "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": "在 Patreon 赞助", + "support_on_liberapay": "在 on 赞助", + "general_sponsors": + "General Sponsors are those that pledged $10 to $39 to Lemmy.", + "crypto": "加密", + "bitcoin": "比特币", + "ethereum": "以太币", + "code": "代码", + "joined": "已加入", + "powered_by": "保留所有权利", + "landing_0": + "Lemmy is a <1>link aggregator / reddit alternative, intended to work in the <2>fediverse.<3>It's self-hostable, has live-updating comment threads, and is tiny (<4>~80kB). Federation into the ActivityPub network is on the roadmap. <5>This is a <6>very early beta version, and a lot of features are currently broken or missing. <7>Suggest new features or report bugs <8>here.<9>Made with <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.", + "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": "节点已存在.", + "community_moderator_already_exists": "节点监管人已存在.", + "community_follower_already_exists": "节点追随者已存在.", + "community_user_already_banned": "节点用户已禁止.", + "couldnt_create_post": "不能创建帖子.", + "couldnt_like_post": "不能收藏帖子.", + "couldnt_find_post": "不能搜寻帖子.", + "couldnt_get_posts": "不能获取帖子", + "couldnt_update_post": "不能更新帖子", + "couldnt_save_post": "不能保持帖子.", + "no_slurs": "和谐.", + "not_an_admin": "不是管理员.", + "site_already_exists": "站点已存在.", + "couldnt_update_site": "不能更新站点.", + "couldnt_find_that_username_or_email": "用户名/邮箱不存在.", + "password_incorrect": "密码不正确.", + "passwords_dont_match": "密码不匹配.", + "admin_already_created": "抱歉,管理员已存在.", + "user_already_exists": "用户已存在.", + "couldnt_update_user": "不可以更新用户.", + "system_err_login": "系统错误. 尝试注销再登录" +} diff --git a/ui/package.json b/ui/package.json index 79756bc69..f49a98fd4 100644 --- a/ui/package.json +++ b/ui/package.json @@ -25,6 +25,7 @@ "emoji-short-name": "^1.0.0", "husky": "^4.2.1", "i18next": "^19.0.3", + "i18next-xhr-backend": "^2.0.0", "inferno": "^7.0.1", "inferno-i18next": "nimbusec-oss/inferno-i18next", "inferno-router": "^7.0.1", diff --git a/ui/src/i18next.ts b/ui/src/i18next.ts index 4311b09f9..36dc6f1d7 100644 --- a/ui/src/i18next.ts +++ b/ui/src/i18next.ts @@ -1,51 +1,24 @@ import i18next from 'i18next'; import { getLanguage } from './utils'; -import { en } from './translations/en'; -import { eo } from './translations/eo'; -import { es } from './translations/es'; -import { de } from './translations/de'; -import { fr } from './translations/fr'; -import { sv } from './translations/sv'; -import { ru } from './translations/ru'; -import { zh } from './translations/zh'; -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'; -import { pt_BR } from './translations/pt_br'; - -// https://github.com/nimbusec-oss/inferno-i18next/blob/master/tests/T.test.js#L66 -const resources = { - en, - eo, - es, - de, - zh, - fr, - sv, - ru, - nl, - it, - fi, - ca, - fa, - pt_BR, -}; +import XHR from 'i18next-xhr-backend'; function format(value: any, format: any, lng: any): any { return format === 'uppercase' ? value.toUpperCase() : value; } -i18next.init({ - debug: false, - // load: 'languageOnly', +i18next + .use(XHR) + .init({ + debug: true, + //load: 'languageOnly', - // initImmediate: false, - lng: getLanguage(), - fallbackLng: 'en', - resources, - interpolation: { format }, + // initImmediate: false, + lng: getLanguage(), + fallbackLng: 'en', + interpolation: { format }, + backend: { + loadPath: '/static/assets/translations/{{lng}}.json', + } }); export { i18next as i18n, resources }; diff --git a/ui/src/translations/ca.ts b/ui/src/translations/ca.ts deleted file mode 100644 index 790a3f9d1..000000000 --- a/ui/src/translations/ca.ts +++ /dev/null @@ -1,239 +0,0 @@ -export const ca = { - translation: { - post: 'Publicar', - remove_post: 'Eliminar publicació', - no_posts: 'Sense publicacions.', - create_a_post: 'Crear una publicació', - create_post: 'Crear Publicació', - number_of_posts: '{{count}} Publicacions', - posts: 'Publicacions', - related_posts: 'Aquestes publicacions podrien estar relacionades', - cross_posts: 'Aquest link també ha sigut publicat en:', - cross_post: 'cross-post', - comments: 'Comentaris', - number_of_comments: '{{count}} Comentaris', - remove_comment: 'Eliminar Comentaris', - communities: 'Comunitats', - users: 'Usuaris', - create_a_community: 'Crear una comunitat', - create_community: 'Crear Comunitat', - remove_community: 'Eliminar Comunitat', - subscribed_to_communities: 'Subscrit a <1>comunitats', - trending_communities: '<1>Comunitats en tendència', - list_of_communities: 'Llista de comunitats', - number_of_communities: '{{count}} Comunitats', - community_reqs: 'minúscules, guió baix, i sense espais.', - create_private_message: 'Crear Missatge Privat', - send_secure_message: 'Enviar Missatge Segur', - send_message: 'Enviar Missatge', - message: 'Missatge', - edit: 'editar', - reply: 'respondre', - cancel: 'Cancelar', - preview: 'Previsualitzar', - upload_image: 'pujar imatge', - avatar: 'Avatar', - upload_avatar: 'Pujar Avatar', - show_avatars: 'Veure Avatares', - formatting_help: 'Ajuda de format', - view_source: 'veure font', - unlock: 'desbloquejar', - lock: 'bloquejar', - sticky: 'fijat', - unsticky: 'no fijat', - link: 'link', - archive_link: 'arxivar link', - mod: 'moderador', - mods: 'moderadores', - moderates: 'Modera', - settings: 'Configuració', - remove_as_mod: 'eliminar com moderador', - appoint_as_mod: 'designar com moderador', - modlog: 'Historial de moderació', - admin: 'administrador', - admins: 'administradors', - remove_as_admin: 'eliminar com administrador', - appoint_as_admin: 'designar com administrador', - remove: 'eliminar', - removed: 'eliminat', - locked: 'bloquejat', - stickied: 'fijat', - reason: 'Raó', - mark_as_read: 'marcar com llegit', - mark_as_unread: 'marcar com no llegit', - delete: 'eliminar', - deleted: 'eliminat', - delete_account: 'Eliminar Compte', - delete_account_confirm: - 'Avís: aquesta acció eliminarà permanentment la teva informació. Introdueix la teva contrasenya per a continuar', - restore: 'restaurar', - ban: 'expulsar', - ban_from_site: 'expulsar del lloc', - unban: 'admetre', - unban_from_site: 'admetre al lloc', - banned: 'expulsat', - save: 'guardar', - unsave: 'descartar', - create: 'crear', - creator: 'creador', - username: "Nom d'Usuari", - email_or_username: 'Correu o Usuari', - number_of_users: '{{count}} Usuaris', - number_of_subscribers: '{{count}} Subscriptors', - number_of_points: '{{count}} Punts', - number_online: '{{count}} Usauris En Línia', - name: 'Nom', - title: 'Titol', - category: 'Categoria', - subscribers: 'Suscriptors', - both: 'Ambdos', - saved: 'Guardat', - unsubscribe: "Desubscriure's", - subscribe: "Subscriure's", - subscribed: 'Subscrit', - prev: 'Anterior', - next: 'Següent', - sidebar: 'Descripció de la comunitat', - sort_type: "Tipus d'orden", - hot: 'Popular', - new: 'Nou', - top_day: 'El millor del dia', - week: 'Setmana', - month: 'Mes', - year: 'Any', - all: 'Tot', - top: 'Millor', - api: 'API', - docs: 'Docs', - inbox: "Bústia d'entrada", - inbox_for: "Bústia d'entrada per a <1>{{user}}", - mark_all_as_read: 'marcar tot com llegit', - type: 'Tipus', - unread: 'No llegit', - replies: 'Respostes', - mentions: 'Menciones', - reply_sent: 'Resposta enviada', - message_sent: 'Missatge enviado', - search: 'Buscar', - overview: 'Resum', - view: 'Vista', - logout: 'Tancar sessió', - login_sign_up: 'Iniciar sessió / Crear compte', - login: 'Iniciar sessió', - sign_up: 'Crear compte', - notifications_error: - "Notificacions d'escriptori no disponibles al teu navegador. Prova amb Firefox o Chrome.", - unread_messages: 'Missatges no llegits', - messages: 'Missatges', - password: 'Contrasenya', - verify_password: 'Verificar Contrasenya', - old_password: 'Antiga Contrasenya', - forgot_password: 'oblidí la meva contrasenya', - reset_password_mail_sent: 'Enviar correu per a restablir la contrasenya.', - password_change: 'Canvi de Contrasenya', - new_password: 'Nueva Contrasenya', - no_email_setup: 'Aquest servidor no ha activat correctament el correu.', - email: 'Correu electrònic', - matrix_user_id: 'Usuari Matricial', - private_message_disclaimer: - 'Avís: Els missatges privats en Lemmy no són segurs. Sisplau creu un compte en <1>Riot.im per a mensajeria segura.', - send_notifications_to_email: 'Enviar notificacions al correu', - optional: 'Opcional', - expires: 'Expira', - language: 'Llenguatge', - browser_default: 'Per defecte del navegador', - downvotes_disabled: 'Vots negatius deshabilitats', - enable_downvotes: 'Habilitar vots negatius', - open_registration: 'Obrir registre', - registration_closed: 'Registre tancat', - enable_nsfw: 'Habilitar NSFW', - url: 'URL', - body: 'Descripció', - copy_suggested_title: 'Copiar el títol sugerido: {{title}}', - community: 'Comunitat', - expand_here: 'Expandir ací', - subscribe_to_communities: "Subscriure's a algunes <1>comunitats.", - chat: 'Chat', - recent_comments: 'Comentaris recients', - no_results: 'Sense resultats.', - setup: 'Configurar', - lemmy_instance_setup: "Configuració d'instancia de Lemmy", - setup_admin: 'Configurar administrador del Lloc', - your_site: 'el teu lloc', - modified: 'modificat', - nsfw: 'NSFW', - show_nsfw: 'Mostrar contingut NSFW', - theme: 'Tema', - sponsors: 'Patrocinadors', - sponsors_of_lemmy: 'Patrocinadors de Lemmy', - sponsor_message: - 'Lemmy és programari lliure i de <1>codi obert, la qual cosa significa que no tindrà publicitats, monetització, ni capitals emprenedors, mai. Les teves donacions secunden directament el desenvolupament a temps complet del projecte. Moltes gràcies a les següents persones:', - support_on_patreon: 'Suport a Patreon', - donate_to_lemmy: 'Donar a Lemmy', - donate: 'Donar', - general_sponsors: - 'Los Patrocinadores Generales son aquellos que señaron entre $10 y $39 a Lemmy.', - crypto: 'Crypto', - bitcoin: 'Bitcoin', - ethereum: 'Ethereum', - monero: 'Monero', - code: 'Codi', - joined: 'Es va unir', - by: 'per', - to: 'a', - from: 'des de', - transfer_community: 'transferir comunitat', - transfer_site: 'transferir lloc', - are_you_sure: 'Ets segur?', - yes: 'sí', - no: 'no', - powered_by: 'Impulsat per', - landing_0: - 'Lemmy és un <1>agregador de links / alternativa a reddit, amb la intenció de funcionar al <2>fedivers.<3>És allotjable per un mateix (sense necessitat de grans companyies), té actualització en directe de cadenes de comentaris, i és petit (<4>~80kB). Federar amb el sistema de xarxes ActivityPub forma part dels objectius del projecte. <5>Aquesta és una <6>versió beta molt prematura, i actualment moltes de les característiques són trencades o falten. <7>Suggereix noves característiques o reporta errors <8>aquí.<9>Fet amb <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.', - not_logged_in: 'No has iniciat sessió.', - logged_in: 'Has iniciat sessió.', - community_ban: "Has sigut expulsat d'aquesta comunitat.", - site_ban: "Has sigut expulsat d'aquest lloc.", - couldnt_create_comment: "No s'ha pogut crear el comentari.", - couldnt_like_comment: "No s'ha pogut donar m'agrada al comentari.", - couldnt_update_comment: "No s'ha pogut actualitzar el comentari.", - couldnt_save_comment: "No s'ha pogut guardar el comentari.", - no_comment_edit_allowed: 'No tens permisos per a editar el comentari.', - no_post_edit_allowed: 'No tens permisos per a editar la publicació.', - no_community_edit_allowed: 'No tens permisos per a editar la comunitat.', - couldnt_find_community: "No s'ha pogut trobar la comunitat.", - couldnt_update_community: "No s'ha pogut actualitzar la comunitat.", - community_already_exists: 'Aquesta comunitat ja existeix.', - community_moderator_already_exists: - 'Aquest moderador de la comunitat ja existeix.', - community_follower_already_exists: - 'Aquest seguidor de la comunitat ja existeix.', - community_user_already_banned: - 'Aquest usuari de la comunitat ja fou expulsat.', - couldnt_create_post: "No s'ha pogut crear la publicació.", - couldnt_like_post: "No s'ha pogut donar m'agrada a la publicació.", - couldnt_find_post: "No s'ha pogut trobar la publicació.", - couldnt_get_posts: "No s'han pogut obtindre les publicacions.", - couldnt_update_post: "No s'ha pogut actualitzar la publicació.", - couldnt_save_post: "No s'ha pogut guardar la publicació.", - no_slurs: 'Prohibit insultar.', - not_an_admin: 'No és un administrador.', - site_already_exists: 'El lloc ja existeix.', - couldnt_update_site: "No s'ha pogut actualitzar el lloc.", - couldnt_find_that_username_or_email: - "No s'ha pogut trobar aquest nom de usuari o correu electrònic.", - password_incorrect: 'Contrasenya incorrecta.', - passwords_dont_match: 'Les contrasenyes no coincideixen.', - admin_already_created: 'Ho sentim, ja hi ha un adminisitrador.', - user_already_exists: "L'usuari ja existeix.", - email_already_exists: 'El correu ja és en ús.', - couldnt_update_user: "No s'ha pogut actualitzar l'usuari.", - system_err_login: - 'Error del sistema. Intenti tancar sessió i ingressar de nou.', - couldnt_create_private_message: "No s'ha pogut crear el missatge privat.", - no_private_message_edit_allowed: - 'Sense permisos per a editar el missatge privat.', - couldnt_update_private_message: - "No s'ha pogut actualitzar el missatge privat.", - }, -}; diff --git a/ui/src/translations/de.ts b/ui/src/translations/de.ts deleted file mode 100644 index d8a4a8500..000000000 --- a/ui/src/translations/de.ts +++ /dev/null @@ -1,210 +0,0 @@ -export const de = { - translation: { - post: 'post', - remove_post: 'Beitrag löschen', - no_posts: 'Keine Beiträge.', - create_a_post: 'Einen Beitrag anlegen', - create_post: 'Beitrag anlegen', - number_of_posts: '{{count}} Beiträge', - posts: 'Beiträge', - related_posts: 'Diese Beiträge könnten verwandt sein', - cross_posts: 'Dieser Link wurde auch veröffentlicht unter:', - cross_post: 'Crosspost', - comments: 'Kommentare', - number_of_comments: '{{count}} Kommentare', - remove_comment: 'Kommentar löschen', - communities: 'Communities', - users: 'Benutzer', - 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', - number_of_communities: '{{count}} Communities', - community_reqs: 'Kleinbuchstaben, Großbuchstaben und keine Leerzeichen.', - edit: 'editieren', - reply: 'antworten', - cancel: 'Abbrechen', - preview: 'Vorschau', - upload_image: 'Bild hochladen', - formatting_help: 'Formatierungshilfe', - view_source: 'Quelle anzeigen', - unlock: 'entsperren', - lock: 'sperren', - sticky: 'haftend', - unsticky: 'nicht haftend', - link: 'link', - archive_link: 'Archiv-Link', - mod: 'Moderator', - mods: 'Moderatoren', - moderates: 'Moderiert', - settings: 'Einstellungen', - remove_as_mod: 'Als Moderator entfernen', - appoint_as_mod: 'Zum Moderator ernennen', - modlog: 'Modlog', - admin: 'Administrator', - admins: 'Administratoren', - remove_as_admin: 'Als Administrator entfernen', - appoint_as_admin: 'Zum Administrator ernennen', - remove: 'entfernen', - removed: 'entfernt', - locked: 'gesperrt', - stickied: 'angeheftet', - reason: 'Grund', - mark_as_read: 'als gelesen markieren', - mark_as_unread: 'als ungelesen markieren', - delete: 'löschen', - deleted: 'gelöscht', - delete_account: 'Konto löschen', - delete_account_confirm: - 'Achtung: Dadurch werden alle Ihre Daten dauerhaft gelöscht. Geben Sie zur Bestätigung Ihr Passwort ein.', - restore: 'wiederherstellen', - ban: 'bannen', - ban_from_site: 'Von der Seite bannen', - unban: 'entbannen', - unban_from_site: 'Von der Seite entbannen', - banned: 'gesperrt', - save: 'speichern', - unsave: 'nicht speichern', - create: 'anlegen', - creator: 'Ersteller', - username: 'Benutzername', - email_or_username: 'E-mail oder Username', - number_of_users: '{{count}} Benutzer', - number_of_subscribers: '{{count}} Abonnenten', - number_of_points: '{{count}} Punkte', - number_online: '{{count}} Benutzer online', - name: 'Name', - title: 'Titel', - category: 'Kategorie', - subscribers: 'Abonnenten', - both: 'Beide', - saved: 'Gespeichert', - unsubscribe: 'Abbestellen', - subscribe: 'Abonnieren', - subscribed: 'Abonniert', - prev: 'Zurück', - next: 'Weiter', - sidebar: 'Seitenleiste', - sort_type: 'Sortieren nach', - hot: 'Hot', - new: 'Neu', - top_day: 'Top täglich', - week: 'Woche', - month: 'Monat', - year: 'Jahr', - all: 'Alle', - top: 'Top', - api: 'API', - inbox: 'Posteingang', - inbox_for: 'Posteingang für <1>{{user}}', - mark_all_as_read: 'Alle als gelesen markieren', - type: 'Typ', - unread: 'Ungelesen', - replies: 'Antworten', - mentions: 'Erwähnung', - reply_sent: 'Antwort gesendet', - search: 'Suchen', - overview: 'Übersicht', - view: 'Ansicht', - logout: 'Ausloggen', - login_sign_up: 'Einloggen / Registrieren', - notifications_error: - 'Desktop-Benachrichtigungen sind in deinem browser nicht verfügbar. Versuche Firefox oder Chrome.', - unread_messages: 'Ungelesene Nachrichten', - password: 'Passwort', - verify_password: 'Passwort überprüfen', - forgot_password: 'Passwort vergessen', - reset_password_mail_sent: - 'Eine E-Mail wurde geschickt, um dein Passwort zurückzusetzen.', - password_change: 'Passwort geändert', - new_password: 'neues Passwort', - no_email_setup: 'Dieser Server hat E-Mails nicht korrekt eingerichtet.', - login: 'Einloggen', - sign_up: 'Registrieren', - 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: 'Gemeinschaft', - expand_here: 'hier erweitern', - subscribe_to_communities: 'Abonniere ein paar <1>communities.', - chat: 'Chat', - recent_comments: 'Neueste Kommentare', - no_results: 'Keine Ergebnisse.', - setup: 'Einrichten', - lemmy_instance_setup: 'Lemmy Instanz Einrichten', - setup_admin: 'Seiten Administrator konfigurieren', - your_site: 'deine Seite', - modified: 'verändert', - nsfw: 'NSFW', - show_nsfw: 'NSFW-Inhalte anzeigen', - theme: 'Aussehen', - sponsors: 'Sponsoren', - sponsors_of_lemmy: 'Sponsoren von Lemmy', - sponsor_message: - 'Lemmy ist freie <1>Open-Source Software, also ohne Werbung, Monetarisierung oder Venturekapital, Punkt. Deine Spenden gehen direkt an die Vollzeit Entwicklung des Projekts. Vielen Dank an die folgenden Personen:', - support_on_patreon: 'Auf Patreon unterstützen', - support_on_liberapay: 'Auf Liberapay unterstützen', - general_sponsors: - 'Allgemeine Sponsoren sind die, die zwischen $10 und $39 zu Lemmy beitragen.', - crypto: 'Kryptowährung', - bitcoin: 'Bitcoin', - ethereum: 'Ethereum', - monero: 'Monero', - code: 'Code', - joined: 'beigetreten', - by: 'von', - to: 'bis', - 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.', - not_logged_in: 'Nicht eingeloggt.', - 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.', - couldnt_update_comment: 'Konnte Kommentar nicht aktualisieren.', - 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 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: - '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.', - couldnt_get_posts: 'Konnte Beiträge nicht holen.', - couldnt_update_post: 'Konnte Beitrag nicht aktualisieren.', - couldnt_save_post: 'Konnte Beitrag nicht speichern.', - no_slurs: 'Keine Beleidigungen.', - not_an_admin: 'Kein Administrator.', - site_already_exists: 'Seite existiert bereits.', - couldnt_update_site: 'Konnte Seite nicht aktualisieren.', - couldnt_find_that_username_or_email: - 'Konnte Username oder E-Mail nicht finden.', - password_incorrect: 'Passwort falsch.', - passwords_dont_match: 'Passwörter stimmen nicht überein.', - admin_already_created: 'Entschuldigung, es gibt schon einen Administrator.', - user_already_exists: 'Nutzer existiert bereits.', - couldnt_update_user: 'Konnte Nutzer nicht aktualisieren', - system_err_login: - 'Systemfehler. Versuche dich aus- und wieder einzuloggen.', - }, -}; diff --git a/ui/src/translations/en.ts b/ui/src/translations/en.ts deleted file mode 100644 index 788bce798..000000000 --- a/ui/src/translations/en.ts +++ /dev/null @@ -1,240 +0,0 @@ -export const en = { - translation: { - post: 'post', - remove_post: 'Remove Post', - no_posts: 'No Posts.', - create_a_post: 'Create a post', - create_post: 'Create Post', - number_of_posts: '{{count}} Posts', - posts: 'Posts', - related_posts: 'These posts might be related', - cross_posts: 'This link has also been posted to:', - cross_post: 'cross-post', - cross_posted_to: 'cross-posted to: ', - comments: 'Comments', - number_of_comments: '{{count}} Comments', - remove_comment: 'Remove Comment', - communities: 'Communities', - users: 'Users', - create_a_community: 'Create a community', - create_community: 'Create Community', - remove_community: 'Remove Community', - subscribed_to_communities: 'Subscribed to <1>communities', - trending_communities: 'Trending <1>communities', - list_of_communities: 'List of communities', - number_of_communities: '{{count}} Communities', - community_reqs: 'lowercase, underscores, and no spaces.', - create_private_message: 'Create Private Message', - send_secure_message: 'Send Secure Message', - send_message: 'Send Message', - message: 'Message', - edit: 'edit', - reply: 'reply', - cancel: 'Cancel', - preview: 'Preview', - upload_image: 'upload image', - avatar: 'Avatar', - upload_avatar: 'Upload Avatar', - show_avatars: 'Show Avatars', - formatting_help: 'formatting help', - view_source: 'view source', - unlock: 'unlock', - lock: 'lock', - sticky: 'sticky', - unsticky: 'unsticky', - link: 'link', - archive_link: 'archive link', - mod: 'mod', - mods: 'mods', - moderates: 'Moderates', - settings: 'Settings', - remove_as_mod: 'remove as mod', - appoint_as_mod: 'appoint as mod', - modlog: 'Modlog', - admin: 'admin', - admins: 'admins', - remove_as_admin: 'remove as admin', - appoint_as_admin: 'appoint as admin', - remove: 'remove', - removed: 'removed', - locked: 'locked', - stickied: 'stickied', - reason: 'Reason', - mark_as_read: 'mark as read', - mark_as_unread: 'mark as unread', - delete: 'delete', - deleted: 'deleted', - delete_account: 'Delete Account', - delete_account_confirm: - 'Warning: this will permanently delete all your data. Enter your password to confirm.', - restore: 'restore', - ban: 'ban', - ban_from_site: 'ban from site', - unban: 'unban', - unban_from_site: 'unban from site', - banned: 'banned', - save: 'save', - unsave: 'unsave', - create: 'create', - creator: 'creator', - username: 'Username', - email_or_username: 'Email or Username', - number_of_users: '{{count}} Users', - number_of_subscribers: '{{count}} Subscribers', - number_of_points: '{{count}} Points', - number_online: '{{count}} Users Online', - name: 'Name', - title: 'Title', - category: 'Category', - subscribers: 'Subscribers', - both: 'Both', - saved: 'Saved', - unsubscribe: 'Unsubscribe', - subscribe: 'Subscribe', - subscribed: 'Subscribed', - prev: 'Prev', - next: 'Next', - sidebar: 'Sidebar', - sort_type: 'Sort type', - hot: 'Hot', - new: 'New', - old: 'Old', - top_day: 'Top day', - week: 'Week', - month: 'Month', - year: 'Year', - all: 'All', - top: 'Top', - api: 'API', - docs: 'Docs', - inbox: 'Inbox', - inbox_for: 'Inbox for <1>{{user}}', - mark_all_as_read: 'mark all as read', - type: 'Type', - unread: 'Unread', - replies: 'Replies', - mentions: 'Mentions', - reply_sent: 'Reply sent', - message_sent: 'Message sent', - search: 'Search', - overview: 'Overview', - view: 'View', - logout: 'Logout', - login_sign_up: 'Login / Sign up', - login: 'Login', - sign_up: 'Sign Up', - notifications_error: - 'Desktop notifications not available in your browser. Try Firefox or Chrome.', - unread_messages: 'Unread Messages', - messages: 'Messages', - password: 'Password', - verify_password: 'Verify Password', - old_password: 'Old Password', - forgot_password: 'forgot password', - reset_password_mail_sent: 'Sent an Email to reset your password.', - password_change: 'Password Change', - new_password: 'New Password', - no_email_setup: "This server hasn't correctly set up email.", - email: 'Email', - matrix_user_id: 'Matrix User', - private_message_disclaimer: - 'Warning: Private messages in Lemmy are not secure. Please create an account on <1>Riot.im for secure messaging.', - send_notifications_to_email: 'Send notifications to Email', - optional: 'Optional', - expires: 'Expires', - language: 'Language', - browser_default: 'Browser Default', - downvotes_disabled: 'Downvotes disabled', - enable_downvotes: 'Enable Downvotes', - open_registration: 'Open Registration', - registration_closed: 'Registration closed', - enable_nsfw: 'Enable NSFW', - url: 'URL', - body: 'Body', - copy_suggested_title: 'copy suggested title: {{title}}', - community: 'Community', - expand_here: 'Expand here', - subscribe_to_communities: 'Subscribe to some <1>communities.', - chat: 'Chat', - recent_comments: 'Recent Comments', - no_results: 'No results.', - setup: 'Setup', - lemmy_instance_setup: 'Lemmy Instance Setup', - setup_admin: 'Set Up Site Administrator', - your_site: 'your site', - modified: 'modified', - nsfw: 'NSFW', - show_nsfw: 'Show NSFW content', - theme: 'Theme', - sponsors: 'Sponsors', - sponsors_of_lemmy: 'Sponsors of 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_liberapay: 'Support on Liberapay', - donate_to_lemmy: 'Donate to Lemmy', - donate: 'Donate', - general_sponsors: - 'General Sponsors are those that pledged $10 to $39 to Lemmy.', - crypto: 'Crypto', - bitcoin: 'Bitcoin', - ethereum: 'Ethereum', - monero: 'Monero', - code: 'Code', - joined: 'Joined', - by: 'by', - to: 'to', - from: 'from', - transfer_community: 'transfer community', - transfer_site: 'transfer site', - are_you_sure: 'are you sure?', - yes: 'yes', - no: 'no', - powered_by: 'Powered by', - landing_0: - "Lemmy is a <1>link aggregator / reddit alternative, intended to work in the <2>fediverse.<3>It's self-hostable, has live-updating comment threads, and is tiny (<4>~80kB). Federation into the ActivityPub network is on the roadmap. <5>This is a <6>very early beta version, and a lot of features are currently broken or missing. <7>Suggest new features or report bugs <8>here.<9>Made with <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.", - not_logged_in: 'Not logged in.', - logged_in: 'Logged in.', - community_ban: 'You have been banned from this community.', - site_ban: 'You have been banned from the site', - couldnt_create_comment: "Couldn't create comment.", - couldnt_like_comment: "Couldn't like comment.", - couldnt_update_comment: "Couldn't update comment.", - couldnt_save_comment: "Couldn't save comment.", - couldnt_get_comments: "Couldn't get comments.", - no_comment_edit_allowed: 'Not allowed to edit comment.', - no_post_edit_allowed: 'Not allowed to edit post.', - no_community_edit_allowed: 'Not allowed to edit community.', - couldnt_find_community: "Couldn't find community.", - couldnt_update_community: "Couldn't update Community.", - community_already_exists: 'Community already exists.', - community_moderator_already_exists: 'Community moderator already exists.', - community_follower_already_exists: 'Community follower already exists.', - community_user_already_banned: 'Community user already banned.', - couldnt_create_post: "Couldn't create post.", - post_title_too_long: 'Post title too long.', - couldnt_like_post: "Couldn't like post.", - couldnt_find_post: "Couldn't find post.", - couldnt_get_posts: "Couldn't get posts", - couldnt_update_post: "Couldn't update post", - couldnt_save_post: "Couldn't save post.", - no_slurs: 'No slurs.', - not_an_admin: 'Not an admin.', - site_already_exists: 'Site already exists.', - couldnt_update_site: "Couldn't update site.", - couldnt_find_that_username_or_email: - "Couldn't find that username or email.", - password_incorrect: 'Password incorrect.', - passwords_dont_match: 'Passwords do not match.', - admin_already_created: "Sorry, there's already an admin.", - user_already_exists: 'User already exists.', - email_already_exists: 'Email already exists.', - couldnt_update_user: "Couldn't update user.", - system_err_login: 'System error. Try logging out and back in.', - couldnt_create_private_message: "Couldn't create private message.", - no_private_message_edit_allowed: 'Not allowed to edit private message.', - couldnt_update_private_message: "Couldn't update private message.", - time: 'Time', - action: 'Action', - }, -}; diff --git a/ui/src/translations/eo.ts b/ui/src/translations/eo.ts deleted file mode 100644 index 5ef33b25c..000000000 --- a/ui/src/translations/eo.ts +++ /dev/null @@ -1,177 +0,0 @@ -export const eo = { - translation: { - post: 'Poŝti', - remove_post: 'Fortiri Poŝton', - no_posts: 'Ne Poŝtoj.', - create_a_post: 'Verki Poŝton', - create_post: 'Verki Poŝton', - number_of_posts: '{{count}} Poŝtoj', - posts: 'Poŝtoj', - related_posts: 'Tiuj poŝtoj eble rilatas', - cross_posts: 'Tiuj ligilo ankaŭ estas poŝtinta al:', - cross_post: 'laŭapoŝto', - comments: 'Komentoj', - number_of_comments: '{{count}} Komentoj', - remove_comment: 'Fortiri Komentojn', - communities: 'Komunumoj', - users: 'Uzantoj', - create_a_community: 'Krei komunumon', - create_community: 'Krei Komunumon', - remove_community: 'Forigi Komunumon', - subscribed_to_communities: 'Abonita al <1>komunumoj', - trending_communities: 'Furora <1>komunumoj', - list_of_communities: 'Listo de komunumoj', - community_reqs: 'minusklaj leteroj, substrekoj, kaj ne spacetoj.', - edit: 'redakti', - reply: 'repliki', - cancel: 'nuligi', - unlock: 'malŝlosi', - lock: 'ŝlosi', - link: 'ligi', - mod: 'moderanto', - mods: 'moderantoj', - moderates: 'Moderigas', - settings: 'Agordoj', - remove_as_mod: 'forigi per moderanto', - appoint_as_mod: 'nomumi per moderanto', - modlog: 'Moderlogo', - admin: 'administranto', - admins: 'administrantoj', - remove_as_admin: 'forigi per administranto', - appoint_as_admin: 'nomumi per administranto', - remove: 'fortiri', - removed: 'fortirita', - locked: 'ŝlosita', - reason: 'Kialo', - mark_as_read: 'marki kiel legita', - mark_as_unread: 'marki kiel nelegita', - delete: 'forigi', - deleted: 'forigita', - restore: 'restaŭri', - ban: 'forbari', - ban_from_site: 'forbari de retejo', - unban: 'malforbari', - unban_from_site: 'malforbari de retejo', - save: 'konservi', - unsave: 'malkonservi', - create: 'krei', - username: 'Uzantnomo', - email_or_username: 'Retadreso aŭ Uzantnomo', - number_of_users: '{{count}} Uzantoj', - number_of_subscribers: '{{count}} Abonantoj', - number_of_points: '{{count}} Voĉdonoj', - name: 'Nomo', - title: 'Titolo', - category: 'Kategorio', - subscribers: 'Abonantoj', - both: 'Ambaŭ', - saved: 'Konservita', - unsubscribe: 'Malaboni', - subscribe: 'Aboni', - subscribed: 'Abonita', - prev: 'Antaŭe', - next: 'Poste', - sidebar: 'Flankstango', - sort_type: 'Klasi per kia', - hot: 'Varmaj', - new: 'Novaj', - top_day: 'Supraj tagaj', - week: 'Semajno', - month: 'Monato', - year: 'Jaro', - all: 'Ĉiam', - top: 'Supraj', - api: 'API', - inbox: 'Ricevujo', - inbox_for: 'Ricevujo de <1>{{user}}', - mark_all_as_read: 'marki ĉiujn kiel legitaj', - type: 'Tipo', - unread: 'Nelegitaj', - reply_sent: 'Repliko sendis', - search: 'Serĉi', - overview: 'Resumo', - view: 'Rigardi', - logout: 'Elsaluti', - login_sign_up: 'Ensaluti / Registriĝi', - login: 'Ensaluti', - sign_up: 'Registriĝi', - notifications_error: - 'Labortablaj avizoj estas nehavebla en via retumilo. Provu Firefox-on aŭ Chrome-on.', - unread_messages: 'Nelegitaj Mesaĝoj', - password: 'Pasvorto', - verify_password: 'Konfirmu Vian Pasvorton', - email: 'Retadreso', - optional: 'Fakultativa', - expires: 'Finiĝos', - url: 'URL', - body: 'Ĉefparto', - copy_suggested_title: 'kopii la sugestiitan titolon: {{title}}', - community: 'Komunumo', - expand_here: 'Ekspansii ĉi tie', - subscribe_to_communities: 'Aboni al iuj <1>komunumoj.', - chat: 'Babilo', - recent_comments: 'Freŝaj Komentoj', - no_results: 'Ne rezultoj.', - setup: 'Agordi', - lemmy_instance_setup: 'Agordi Instancon de Lemmy', - setup_admin: 'Agordi Retejan Administranton', - your_site: 'via retejo', - modified: 'modifita', - nsfw: 'NSFW', - show_nsfw: 'Vidigi NSFW-an enhavon', - sponsors: 'Subtenantoj', - sponsors_of_lemmy: 'Subtenantoj de Lemmy', - sponsor_message: - 'Lemmy estas senpaga, <1>liberkoda programaro. Tio signifas ne reklami, pagigi, aŭ riska kapitalo, ĉiam. Viaj donacoj rekte subtenas plentempan evoluon de la projekto. Dankon al tiuj homoj:', - support_on_patreon: 'Subteni per Patreon', - general_sponsors: - 'Ĝeneralaj Subtenantoj estas tiuj ke donacis inter $10 kaj $39 al Lemmy.', - crypto: 'Crypto', - bitcoin: 'Bitcoin', - ethereum: 'Ethereum', - monero: 'Monero', - code: 'Kodo', - joined: 'Unuiĝis', - by: 'de', - to: 'al', - transfer_community: 'transdoni la komunumon', - transfer_site: 'transdoni la retejon', - powered_by: 'Konstruis per', - landing_0: - 'Lemmy estas <1>ligila agregatilo / Reddit anstataŭo ke intenciĝas funkci en la <2>federacio-universo.<3>ĝi estas mem-gastigebla, havas nuna-ĝisdatigajn komentarojn, kaj estas malgrandega (<4>~80kB). Federacio en la ActivityPub-an reton estas planizita. <5>Estas <6>fruega beta versio, kaj multaj trajtoj estas nune difektaj aŭ mankaj. <7>Sugestias novajn trajtojn aŭ raportas cimojn <8>ĉi tie.<9>Faris per <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.', - not_logged_in: 'Ne estas ensalutinta.', - community_ban: 'Vi estas forbarita de la komunumo.', - site_ban: 'Vi estas forbarita de la retejo', - couldnt_create_comment: 'Ne povis krei la komenton.', - couldnt_like_comment: 'Ne povis ŝati la komenton.', - couldnt_update_comment: 'Ne povis ĝisdatigi komenton.', - couldnt_save_comment: 'Ne povis konservi komenton.', - no_comment_edit_allowed: 'Ne rajtas redakti la komenton.', - no_post_edit_allowed: 'Ne rajtas redakti la poŝton.', - no_community_edit_allowed: 'Ne rajtas redakti la komunumon.', - couldnt_find_community: 'Ne povis trovi la komunumon.', - couldnt_update_community: 'Ne povis ĝisdatigi la komunumon.', - community_already_exists: 'Komunumo jam ekzistas.', - community_moderator_already_exists: 'Komunuma moderanto jam ekzistas.', - community_follower_already_exists: 'Komunuma sekvanto.', - community_user_already_banned: 'Komunuma uzanto jam estas forbarita.', - couldnt_create_post: 'Ne povis krei la poŝton.', - couldnt_like_post: 'Ne povis ŝati la poŝton.', - couldnt_find_post: 'Ne povis trovi la poŝton.', - couldnt_get_posts: 'Ne povis irpreni poŝtojn', - couldnt_update_post: 'Ne povis ĝisdatigi la poŝton', - couldnt_save_post: 'Ne povis konservi la poŝton.', - no_slurs: 'Ne bigotaj vortoj.', - not_an_admin: 'Ne estas administranto.', - site_already_exists: 'Retejo jam ekzistas.', - couldnt_update_site: 'Ne povis ĝisdatigi la retejon.', - couldnt_find_that_username_or_email: - 'Ne povis trovi tiun uzantnomon aŭ retadreson.', - password_incorrect: 'Pasvorto malĝustas.', - passwords_dont_match: 'Pasvortoj ne samas.', - admin_already_created: 'Pardonu, jam estas administranto.', - user_already_exists: 'Uzanto jam ekzistas.', - couldnt_update_user: 'Ne povis ĝisdatigi la uzanton.', - system_err_login: 'Sistema eraro. Provu elsaluti kaj ensaluti.', - }, -}; diff --git a/ui/src/translations/es.ts b/ui/src/translations/es.ts deleted file mode 100644 index 1b1b5d67c..000000000 --- a/ui/src/translations/es.ts +++ /dev/null @@ -1,242 +0,0 @@ -export const es = { - translation: { - post: 'Publicar', - remove_post: 'Eliminar publicación', - no_posts: 'Sin publicaciones.', - create_a_post: 'Crear una publicación', - create_post: 'Crear Publicación', - number_of_posts: '{{count}} Publicaciones', - posts: 'Publicaciones', - related_posts: 'Estas publicaciones podrían estar relacionadas', - cross_posts: 'Este link también ha sido publicado en:', - cross_post: 'cross-post', - comments: 'Comentarios', - number_of_comments: '{{count}} Comentarios', - remove_comment: 'Eliminar Comentarios', - communities: 'Comunidades', - users: 'Usuarios', - create_a_community: 'Crear una comunidad', - create_community: 'Crear Comunidad', - remove_community: 'Eliminar Comunidad', - subscribed_to_communities: 'Suscrito a <1>comunidades', - trending_communities: '<1>Comunidades en tendencia', - list_of_communities: 'Lista de comunidades', - number_of_communities: '{{count}} Comunidades', - community_reqs: 'minúsculas, guión bajo, y sin espacios.', - create_private_message: 'Crear Mensaje Privado', - send_secure_message: 'Enviar Mensaje Seguro', - send_message: 'Enviar Mensaje', - message: 'Mensaje', - edit: 'editar', - reply: 'responder', - cancel: 'Cancelar', - preview: 'Previsualizar', - upload_image: 'subir imagen', - avatar: 'Avatar', - upload_avatar: 'Subir Avatar', - show_avatars: 'Ver Avatares', - formatting_help: 'Ayuda de formato', - view_source: 'ver fuente', - unlock: 'desbloquear', - lock: 'bloquear', - sticky: 'fijado', - unsticky: 'no fijado', - link: 'link', - archive_link: 'archivar link', - mod: 'moderador', - mods: 'moderadores', - moderates: 'Modera', - settings: 'Configuración', - remove_as_mod: 'eliminar como moderador', - appoint_as_mod: 'designar como moderador', - modlog: 'Historial de moderación', - admin: 'administrador', - admins: 'administradores', - remove_as_admin: 'eliminar como administrador', - appoint_as_admin: 'designar como administrador', - remove: 'eliminar', - removed: 'eliminado', - locked: 'bloqueado', - stickied: 'fijado', - reason: 'Razón', - mark_as_read: 'marcar como leído', - mark_as_unread: 'marcar como no leído', - delete: 'eliminar', - deleted: 'eliminado', - delete_account: 'Eliminar Cuenta', - delete_account_confirm: - 'Aviso: esta acción eliminará permanentemente tu información. Introduce tu contraseña para continuar', - restore: 'restaurar', - ban: 'expulsar', - ban_from_site: 'expulsar del sitio', - unban: 'admitir', - unban_from_site: 'admitir en el sitio', - banned: 'expulsado', - save: 'guardar', - unsave: 'descartar', - create: 'crear', - creator: 'creador', - username: 'Nombre de Usuario', - email_or_username: 'Correo o Usuario', - number_of_users: '{{count}} Usuarios', - number_of_subscribers: '{{count}} Suscriptores', - number_of_points: '{{count}} Puntos', - number_online: '{{count}} Usuarios En Línea', - name: 'Nombre', - title: 'Titulo', - category: 'Categoría', - subscribers: 'Suscriptores', - both: 'Ambos', - saved: 'Guardado', - unsubscribe: 'Desuscribirse', - subscribe: 'Suscribirse', - subscribed: 'Suscrito', - prev: 'Anterior', - next: 'Siguiente', - sidebar: 'Descripción de la comunidad', - sort_type: 'Tipo de orden', - hot: 'Popular', - new: 'Nuevo', - top_day: 'Lo mejor del día', - week: 'Semana', - month: 'Mes', - year: 'Año', - all: 'Todo', - top: 'Mejor', - api: 'API', - docs: 'Docs', - inbox: 'Buzón de entrada', - inbox_for: 'Buzón de entrada para <1>{{user}}', - mark_all_as_read: 'marcar todo como leído', - type: 'Tipo', - unread: 'No leído', - replies: 'Respuestas', - mentions: 'Menciones', - reply_sent: 'Respuesta enviada', - message_sent: 'Mensaje enviado', - search: 'Buscar', - overview: 'Resumen', - view: 'Vista', - logout: 'Cerrar sesión', - login_sign_up: 'Iniciar sesión / Crear cuenta', - login: 'Iniciar sesión', - sign_up: 'Crear cuenta', - notifications_error: - 'Notificaciones de escritorio no disponibles en tu navegador. Prueba Firefox o Chrome.', - unread_messages: 'Mensajes no leídos', - messages: 'Mensajes', - password: 'Contraseña', - verify_password: 'Verificar contraseña', - old_password: 'Antigua Contraseña', - forgot_password: 'olvidé mi contraseña', - reset_password_mail_sent: 'Enviar correo para reestablecer la contraseña.', - password_change: 'Cambio de Contraseña', - new_password: 'Nueva Contraseña', - no_email_setup: 'Este servidor no ha activado correctamente el correo.', - email: 'Correo electrónico', - matrix_user_id: 'Usuario Matricial', - private_message_disclaimer: - 'Aviso: Los mensajes privados en Lemmy no son seguros. Por favor cree una cuenta en <1>Riot.im para mensajeria segura.', - send_notifications_to_email: 'Enviar notificaciones al correo', - optional: 'Opcional', - expires: 'Expira', - language: 'Idioma', - browser_default: 'Por defecto del navegador', - downvotes_disabled: 'Votos negativos deshabilitados', - enable_downvotes: 'Habilitar votos negativos', - open_registration: 'Abrir registro', - registration_closed: 'Registro cerrado', - enable_nsfw: 'Habilitar NSFW', - url: 'URL', - body: 'Descripción', - copy_suggested_title: 'Copiar el título sugerido: {{title}}', - community: 'Comunidad', - expand_here: 'Expandir aquí', - subscribe_to_communities: 'Suscribirse a algunas <1>comunidades.', - chat: 'Chat', - recent_comments: 'Comentarios recientes', - no_results: 'Sin resultados.', - setup: 'Configurar', - lemmy_instance_setup: 'Configuración de instancia de Lemmy', - setup_admin: 'Configurar administrador del Sitio', - your_site: 'tu sitio', - modified: 'modificado', - nsfw: 'NSFW', - show_nsfw: 'Mostrar contenido NSFW', - theme: 'Tema', - sponsors: 'Patrocinadores', - sponsors_of_lemmy: 'Patrocinadores de Lemmy', - sponsor_message: - 'Lemmy es software libre y de <1>código abierto, lo que significa que no tendrá publicidades, monetización, ni capitales emprendedores, nunca. Tus donaciones apoyan directamente el desarrollo a tiempo completo del proyecto. Muchas gracias a las siguientes personas:', - support_on_patreon: 'Apoyo en Patreon', - support_on_liberapay: 'Apoyo en Liberapay', - donate_to_lemmy: 'Donar a Lemmy', - donate: 'Donar', - general_sponsors: - 'Los Patrocinadores Generales son aquellos que señaron entre $10 y $39 a Lemmy.', - crypto: 'Crypto', - bitcoin: 'Bitcoin', - ethereum: 'Ethereum', - monero: 'Monero', - code: 'Código', - joined: 'Se unió', - by: 'por', - to: 'a', - from: 'desde', - transfer_community: 'transferir comunidad', - transfer_site: 'transferir sitio', - are_you_sure: '¿Estás seguro?', - yes: 'sí', - no: 'no', - powered_by: 'Impulsado por', - landing_0: - 'Lemmy es un <1>agregador de links / alternativa a reddit, con la intención de funcionar en el <2>fediverso.<3>Es alojable por uno mismo (sin necesidad de grandes compañías), tiene actualización en vivo de cadenas de comentarios, y es pequeño (<4>~80kB). Federar con el sistema de redes ActivityPub forma parte de los objetivos del proyecto. <5>Esta es una <6>version beta muy prematura, y actualmente muchas de las características están rotas o faltan. <7>Sugiere nuevas características o reporta errores <8>aquí.<9>Hecho con <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.', - not_logged_in: 'No has iniciado sesión.', - logged_in: 'Has iniciado sesión.', - community_ban: 'Has sido expulsado de esta comunidad.', - site_ban: 'Has sido expulsado del sitio', - couldnt_create_comment: 'No se pudo crear el comentario.', - couldnt_like_comment: 'No se pudo dar me gusta al comentario.', - couldnt_update_comment: 'No se pudo actualizar el comentario.', - couldnt_save_comment: 'No se pudo guardar el comentario.', - no_comment_edit_allowed: 'No tiene permisos para editar el comentario.', - no_post_edit_allowed: 'No tiene permisos para editar la publicación.', - no_community_edit_allowed: 'No tiene permisos para editar la comunidad.', - couldnt_find_community: 'No se pudo encontrar la comunidad.', - couldnt_update_community: 'No se pudo actualizar la comunidad.', - community_already_exists: 'Esta comunidad ya existe.', - community_moderator_already_exists: - 'Este moderador de la comunidad ya existe.', - community_follower_already_exists: - 'Este seguidor de la comunidad ya existe.', - community_user_already_banned: - 'Este usuario de la comunidad ya fue expulsado.', - couldnt_create_post: 'No se pudo crear la publicación.', - couldnt_like_post: 'No se pudo gustar la publicación.', - couldnt_find_post: 'No se pudo encontrar la publicación.', - couldnt_get_posts: 'No se pudo obtener las publicaciones', - couldnt_update_post: 'No se pudo actualizar la publicación', - couldnt_save_post: 'No se pudo guardar la publicación.', - no_slurs: 'Prohibido insultar.', - not_an_admin: 'No es un administrador.', - site_already_exists: 'El sitio ya existe.', - couldnt_update_site: 'No se pudo actualizar el sitio.', - couldnt_find_that_username_or_email: - 'No se pudo encontrar ese nombre de usuario o correo electrónico.', - password_incorrect: 'Contraseña incorrecta.', - passwords_dont_match: 'Las contraseñas no coinciden.', - admin_already_created: 'Lo sentimos, ya hay un adminisitrador.', - user_already_exists: 'El usuario ya existe.', - email_already_exists: 'El correo ya está en uso.', - couldnt_update_user: 'No se pudo actualizar el usuario.', - system_err_login: - 'Error del sistema. Intente cerrar sesión e ingresar de nuevo.', - couldnt_create_private_message: 'No se pudo crear el mensaje privado.', - no_private_message_edit_allowed: - 'Sin permisos para editar el mensaje privado.', - couldnt_update_private_message: 'No se pudo actualizar el mensaje privado.', - old: 'Antiguo', - time: 'Tiempo', - action: 'Acción', - }, -}; diff --git a/ui/src/translations/fa.ts b/ui/src/translations/fa.ts deleted file mode 100644 index c06045c69..000000000 --- a/ui/src/translations/fa.ts +++ /dev/null @@ -1,169 +0,0 @@ -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/fi.ts b/ui/src/translations/fi.ts deleted file mode 100644 index 32205a564..000000000 --- a/ui/src/translations/fi.ts +++ /dev/null @@ -1,236 +0,0 @@ -export const fi = { - translation: { - post: 'viesti', - remove_post: 'Poista viesti', - no_posts: 'Ei viestjä.', - create_a_post: 'Luo viesti', - create_post: 'Luo viesti', - number_of_posts: '{{count}} viestiä', - posts: 'Viestit', - related_posts: 'Nämä viestit voivat liittyä toisiinsa', - cross_posts: 'Tämä linkki on jaettu:', - cross_post: 'jaa ristiin', - comments: 'Kommentit', - number_of_comments: '{{count}} kommenttia', - remove_comment: 'Poista kommentti', - communities: 'Yhteisöt', - users: 'Käyttäjät', - create_a_community: 'Luo yhteisö', - create_community: 'Luo yhteisö', - remove_community: 'Poista yhteisö', - subscribed_to_communities: 'Tilatut <1>yhteisöt', - trending_communities: 'Nousevat <1>yhteisöt', - list_of_communities: 'Lista yhteisöistä', - number_of_communities: '{{count}} yhteisöä', - community_reqs: - 'pienillä kirjaimilla, alleviivauksella, eikä välilyöntejä.', - create_private_message: 'Luo yksityisviesti', - send_secure_message: 'Lähetä suojattu viesti', - send_message: 'Lähetä viesti', - message: 'Viesti', - edit: 'muokkaa', - reply: 'vastaa', - cancel: 'Peru', - preview: 'Esikatselu', - upload_image: 'lataa kuva', - avatar: 'avatar', - upload_avatar: 'Lähetä avatar', - show_avatars: 'Näytä avatarit', - formatting_help: 'apua muotoiluun', - view_source: 'näytä lähde', - unlock: 'avaa', - lock: 'lukitse', - sticky: 'kiinnitä', - unsticky: 'poista kiinnitys', - link: 'linkitä', - archive_link: 'arkistoi linkki', - mod: 'moderaattori', - mods: 'moderaattorit', - moderates: 'Moderoi', - settings: 'Asetukset', - remove_as_mod: 'Poista moderaattorina', - appoint_as_mod: 'Nimitä moderaattoriksi', - modlog: 'Moderoinnin loki', - admin: 'Ylläpitäjä', - admins: 'ylläpitäjät', - remove_as_admin: 'poista ylläpitäjänä', - appoint_as_admin: 'nimitä ylläpitäjäksi', - remove: 'poista', - removed: 'poistettu', - locked: 'lukittu', - stickied: 'kiinnitetty', - reason: 'Syy', - mark_as_read: 'merkitse luetuksi', - mark_as_unread: 'merkitse lukemattomaksi', - delete: 'poista', - deleted: 'deleted', - delete_account: 'Poista tili', - delete_account_confirm: - 'Varoitus: tämä poistaa pysyvästi kaiken datasi. Anna salasanasi varmistukseksi.', - restore: 'palauta', - ban: 'porttikielto', - ban_from_site: 'aseta porttikielto sivulle', - unban: 'poista porttikielto', - unban_from_site: 'poista porttikielto sivulta', - banned: 'asetettu porttikieltoon', - save: 'tallenna', - unsave: 'jätä tallentamatta', - create: 'luo', - creator: 'luoja', - username: 'Käyttäjänimi', - email_or_username: 'Sähköposti tai käyttäjätunnus', - number_of_users: '{{count}} käyttäjää', - number_of_subscribers: '{{count}} tilaajaa', - number_of_points: '{{count}} pistettä', - number_online: '{{count}} käyttäjää aktiivisena', - name: 'Nimi', - title: 'Kuvaus', - category: 'Luokka', - subscribers: 'Tilaajat', - both: 'Molemmat', - saved: 'Tallennettu', - unsubscribe: 'Poista tilaus', - subscribe: 'Tilaa', - subscribed: 'Tilattu', - prev: 'Edellinen', - next: 'Seuraava', - sidebar: 'Sivupalkki', - sort_type: 'Lajittele tyypin mukaan', - hot: 'Kuumat', - new: 'Uudet', - top_day: 'Päivän parhaimmat', - week: 'Viikko', - month: 'Kuukausi', - year: 'Vuosi', - all: 'Kaikki', - top: 'Parhaimmat', - api: 'API', - docs: 'Dokumentaatio', - inbox: 'Postilaatikko', - inbox_for: 'Postilaatikko käyttäjällä <1>{{user}}', - mark_all_as_read: 'aseta kaikki luetuiksi', - type: 'Tyyppi', - unread: 'Lukematon', - replies: 'Vastaukset', - mentions: 'Maininnat', - reply_sent: 'Vastaus lähetetty', - message_sent: 'Viesti lähetetty', - search: 'Etsi', - overview: 'Yleiskatsaus', - view: 'Katso', - logout: 'Kirjaudu ulos', - login_sign_up: 'Kirjaudu sisään / Rekisteröidy', - login: 'Kirjaudu sisään', - sign_up: 'Rekisteröidy', - notifications_error: - 'Työpöydän ilmoitukset eivät ole saatavilla selaimellesi. Yritä Firefoxia tai Chromea.', - unread_messages: 'Lukemattomat viestit', - messages: 'Viestit', - password: 'Salasana', - verify_password: 'Vahvista salasana', - old_password: 'Vanha salasana', - forgot_password: 'unohdin salasanani', - reset_password_mail_sent: 'Sähköposti lähetettiin salasanan nollaamiseksi.', - password_change: 'Salasanan muutos', - new_password: 'Uusi salasana', - no_email_setup: 'Tämä palvelin ei ole asettanut sähköpostia oikein.', - email: 'Sähköposti', - matrix_user_id: ' Matrix-käyttäjä', - private_message_disclaimer: - 'Varoitus: Yksityisviestit Lemmyssä eivät ole turvallisia. Luo tili <1>Riot.im -palveluun turvallista viestintää varten.', - send_notifications_to_email: 'Lähetä ilmoitukset sähköpostiin', - optional: 'Valinnainen', - expires: 'Umpeutuu', - language: 'Kieli', - browser_default: 'Selaimen oletus', - downvotes_disabled: 'Alaäänet otettu pois päältä', - enable_downvotes: 'Salli alaäänet', - open_registration: 'Avaa rekisteröityminen', - registration_closed: 'Rekisteröityminen suljettu', - enable_nsfw: 'Salli NSFW', - url: 'URL', - body: 'Body', - copy_suggested_title: 'kopioi ehdotettu otsikko: {{title}}', - community: 'Yhteisö', - expand_here: 'Laajenna tässä', - subscribe_to_communities: 'Tilaa joitakin <1>yhteisöjä.', - chat: 'Chat', - recent_comments: 'Viimeaikaiset kommentit', - no_results: 'Ei tuloksia.', - setup: 'Asetus', - lemmy_instance_setup: 'Lemmy-instanssin asetus', - setup_admin: 'Aseta sivuston ylläpitäjä', - your_site: 'sivustosi', - modified: 'muokattu', - nsfw: 'NSFW', - show_nsfw: 'Näytä NSFW-sisältö', - theme: 'Teema', - sponsors: 'Sponsorit', - sponsors_of_lemmy: 'Lemmy-sponsorit', - sponsor_message: - 'Lemmy on vapaa, <1>avoimen lähdekoodin -ohjelmisto, eli mainontaa, rahantekemistä, tai pääomasijoitusta täällä ei tule ikinä olemaan. Lahjoituksesi tukevat suoraan projektin täysipäiväistä kehitystä. Kiitokset seuraaville ihmisille:', - support_on_patreon: 'Tue Patreonissa', - donate_to_lemmy: 'Lahjoita Lemmylle', - donate: 'Lahjoita', - general_sponsors: - 'Yleisiä sponsoreja ovat he, jotka lupaavat 10-39 dollaria Lemmylle.', - crypto: 'Crypto', - bitcoin: 'Bitcoin', - ethereum: 'Ethereum', - monero: 'Monero', - code: 'Code', - joined: 'Liittyi', - by: 'käyttäjältä', - to: 'yhteisössä', - from: 'paikasta', - transfer_community: 'siirron yhteisö', - transfer_site: 'siirron määrä', - are_you_sure: 'oletko varma?', - yes: 'kyllä', - no: 'ei', - powered_by: 'Vauhdittajana', - landing_0: - 'Lemmy on <1>linkinkerääjä / Reddit-vaihtoehto, tarkoitettu toimimaan <2>fediversessä.<3>Sitä voi isännöidä itse, siinä on tosiaikaisesti päivittyvät kommenttiketjut, ja se on pieni (<4>~80 kilotavua). Federointi ActivityPub-verkkoon on suunnittelun alla. <5>Tämä on <6>hyvin varhainen betaversio, ja monet ominaisuudet ovat toistaiseksi rikki tai poissa. <7>Ehdota uusia ominaisuuksia tai raportoi bugeja <8>tänne.<9>Tehty teknologioilla <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.', - not_logged_in: 'Ei kirjautunut sisään.', - logged_in: 'Kirjautunut sisään.', - community_ban: 'Sinulle on asetettu porttikielto tähän yhteisöön.', - site_ban: 'Sinut on asetettu porttikieltoon tältä sivustolta', - couldnt_create_comment: 'Kommenttia ei pystytty luomaan.', - couldnt_like_comment: 'Kommentista ei voitu tykätä.', - couldnt_update_comment: 'Kommenttia ei voitu päivittää.', - couldnt_save_comment: 'Kommenttia ei voitu tallentaa.', - no_comment_edit_allowed: 'Et ole sallittu muokkaamaan kommenttia.', - no_post_edit_allowed: 'Et ole sallittu muokkaamaan viestiä.', - no_community_edit_allowed: 'Et ole sallittu muokkaamaan yhteisöä.', - couldnt_find_community: 'Yhteisöä ei voitu löytää.', - couldnt_update_community: 'Yhteisöä ei voitu päivittää.', - community_already_exists: 'Yhteisö on jo olemassa.', - community_moderator_already_exists: 'Yhteisön moderaattori on jo olemassa.', - community_follower_already_exists: 'Yhteisön seuraaja on jo olemassa.', - community_user_already_banned: 'Yhteisön käyttäjä on jo porttikiellossa.', - couldnt_create_post: 'Ei voitu luoda viestiä.', - couldnt_like_post: 'Viestistä ei voitu tykätä.', - couldnt_find_post: 'Viestiä ei löytynyt.', - couldnt_get_posts: 'Viestejä ei saatu', - couldnt_update_post: 'Viestiä ei voitu päivittää', - couldnt_save_post: 'Viestiä ei voitu tallentaa.', - no_slurs: 'Ei loukkauksia.', - not_an_admin: 'Ei ole ylläpitäjä.', - site_already_exists: 'Sivusto on jo olemassa.', - couldnt_update_site: 'Sivustoa ei voitu päivittää.', - couldnt_find_that_username_or_email: - 'Käyttäjänimeä tai sähköpostia ei onnistuttu löytämään.', - password_incorrect: 'Salasana on väärin.', - passwords_dont_match: 'Salasanat eivät täsmää.', - admin_already_created: 'Anteeksi, mutta täällä on jo ylläpitäjä.', - user_already_exists: 'Käyttäjä on jo olemassa.', - email_already_exists: 'Sähköposti on jo olemassa.', - couldnt_update_user: 'Käyttäjää ei voitu päivittää.', - system_err_login: - 'Järjestelmävirhe. Yritä kirjautua ulos ja kirjautua uudestaan sisään.', - couldnt_create_private_message: 'Yksityisviestiä ei voitu luoda.', - no_private_message_edit_allowed: - 'Et ole sallittu muokkaamaan yksityisviestiä.', - couldnt_update_private_message: 'Yksityisviestiä ei voitu päivittää.', - }, -}; diff --git a/ui/src/translations/fr.ts b/ui/src/translations/fr.ts deleted file mode 100644 index 7a957271b..000000000 --- a/ui/src/translations/fr.ts +++ /dev/null @@ -1,242 +0,0 @@ -export const fr = { - translation: { - post: 'publication', - remove_post: 'Supprimer la publication', - no_posts: 'Pas de publications.', - create_a_post: 'Créer une publication', - create_post: 'Créer la publication', - number_of_posts: '{{count}} Publications', - posts: 'Publications', - related_posts: 'Ces sujets peuvent être corrélés', - cross_posts: 'Ce sujet a également été posté sur :', - cross_post: 'publication croisée', - cross_posted_to: 'publication croisée à', - comments: 'Commentaires', - number_of_comments: '{{count}} Commentaires', - remove_comment: 'Supprimer le commentaire', - communities: 'Communautés', - users: 'Utilisateurs', - create_a_community: 'Créer une communauté', - create_community: 'Créer la communauté', - remove_community: 'Supprimer la Communauté', - subscribed_to_communities: 'Abonné à ces <1>communautés', - trending_communities: '<1>Communautés appréciées', - list_of_communities: 'Liste des communautés', - number_of_communities: '{{count}} communautés', - community_reqs: 'en minuscule, sans espace et avec tiret du bas.', - create_private_message: 'Créer un message privé', - send_secure_message: 'Envoyer le message sécurisé', - send_message: 'Enovyer le message', - message: 'Message', - edit: 'éditer', - reply: 'répondre', - cancel: 'Annuler', - preview: 'prévisualiser', - upload_image: 'envoyer une image', - avatar: 'Avatar', - upload_avatar: 'Télécharger une avatar', - show_avatars: 'Afficher les avatars', - formatting_help: 'aide au formattage', - view_source: 'voir la source', - unlock: 'débloquer', - lock: 'bloquer', - sticky: 'épingler', - unsticky: 'décrocher', - link: 'lien', - archive_link: 'archiver le lien', - mod: 'modérateur', - mods: 'modérateurs', - moderates: 'Modérer', - settings: 'Paramètres', - remove_as_mod: 'Supprimer comme modérateur', - appoint_as_mod: 'Nommer comme modérateur', - modlog: 'Historique de modération', - admin: 'admin', - admins: 'admins', - remove_as_admin: 'Supprimer comme admin', - appoint_as_admin: 'Nommer comme admin', - remove: 'retirer', - removed: 'retiré', - locked: 'bloqué', - stickied: 'épinglé', - reason: 'Raison', - mark_as_read: 'marquer comme lu', - mark_as_unread: 'marquer comme non-lu', - delete: 'supprimer', - deleted: 'supprimé', - delete_account: 'Supprimer le compte', - delete_account_confirm: - 'Attention: cette action supprime toutes vos données de façons permanente ! Entrez votre mot de passe pour confirmer.', - restore: 'restaurer', - ban: 'bannir', - ban_from_site: 'bannir du site', - unban: 'pardon', - unban_from_site: 'faire revenir sur le site', - banned: 'banni', - save: 'sauvegarder', - unsave: 'retirer', - create: 'créer', - creator: 'createur', - username: "Nom d'utilisateur", - email_or_username: "Email ou Nom d'utilisateur", - number_of_users: '{{count}} Utilisateurs', - number_of_subscribers: '{{count}} Abonnés', - number_of_points: '{{count}} Points', - number_online: '{{count}} Utilisateurs en ligne', - name: 'Nom', - title: 'Titre', - category: 'Catégorie', - subscribers: 'Abonnés', - both: 'Les deux', - saved: 'Sauvegardé', - unsubscribe: 'Se désabonner', - subscribe: "S'abonner", - subscribed: 'Abonnés', - prev: 'Précédent', - next: 'Suivant', - sidebar: 'Texte latéral', - sort_type: 'Trier', - hot: 'Tendances', - new: 'Nouveaux', - old: 'Ancien', - top_day: 'Top du jour', - week: 'Semaine', - month: 'Mois', - year: 'Année', - all: 'Tout', - top: 'Top', - api: 'API', - docs: 'Documentations', - inbox: 'Boîte de réception', - inbox_for: 'Boîte de réception de <1>{{user}}', - mark_all_as_read: 'Tout marquer comme lu', - type: 'Type', - unread: 'Non-lu', - replies: 'Réponses', - mentions: 'Mentions', - reply_sent: 'Réponse envoyée', - message_sent: 'Message envoyé', - search: 'Rechercher', - overview: 'Général', - view: 'Voir', - logout: 'Se déconnecter', - login_sign_up: "Se connecter / S'inscrire", - login: 'Se connecter', - sign_up: "S'inscrire", - notifications_error: - 'Les notifications de bureau ne sont pas discponibles sur votre navigateur. Essayez Firefox ou Chrome.', - unread_messages: 'Messages non-lu', - messages: 'Messages', - password: 'Mot de passe', - verify_password: 'Vérifiez le mot de passe', - old_password: 'Ancien mot de passe', - forgot_password: 'Mot de passe oublié', - reset_password_mail_sent: 'Un email a été envoyé pour réinitialiser votre mot de passe.', - password_change: 'Changement de mot de passe', - new_password: 'Nouveau mot de passe', - no_email_setup: "Ce serveur n'a pas correctement configuré la messagerie de email.", - email: 'Email', - matrix_user_id: 'Utilisateur Matrix', - private_message_disclaimer: - "Attention: les messages privés en Matrix ne sont pas sécurisés. S'il vous plait, créer un compte de <1>Riot.im pour des messages sécurisés.", - send_notifications_to_email: 'Envoyer des notifications par email', - optional: 'Optionnel', - expires: 'Expire', - language: 'Langue', - browser_default: 'Défaut pour le navigateur', - downvotes_disabled: 'Votes négatifs désactivés', - enable_downvotes: 'Votes négatifs activés', - open_registration: 'Ouvrir la regestration', - registration_closed: 'Régestration fermée', - enable_nsfw: 'Activer NSFW', - url: 'URL', - body: 'Texte', - copy_suggested_title: 'Ajouter le titre suggéré: {{title}}', - community: 'Communauté', - expand_here: 'Développer ici', - subscribe_to_communities: "S'abonner à quelques <1>communautés.", - chat: 'Chat', - recent_comments: 'Commentaires récents', - no_results: 'Pas de résultats.', - setup: 'Installation', - lemmy_instance_setup: "Installation d'une instance Lemmy", - setup_admin: 'Créer un administrateur', - your_site: 'votre site', - modified: 'modifié', - nsfw: 'Pas sûr pour le travail', - show_nsfw: 'Afficher le contenu NSFW', - theme: 'Thème', - sponsors: 'Sponsors', - sponsors_of_lemmy: 'Sponsors de Lemmy', - sponsor_message: - "Lemmy est gratuit et <1>open-source, c'est à dire sans publicité et sans monétisation. Pour toujours. Vos dons soutiennent directement le développement du projet. Merci à nos soutiens.", - support_on_patreon: 'Soutenir sur Patreon', - support_on_liberapay: 'Soutenir sur Liberapay', - donate_to_lemmy: 'Faire un don à Lemmy', - donate: 'Faire un don', - general_sponsors: 'Les sponsors généraux sont ceux garantissant de 10 à 39$.', - crypto: 'Cryptomonnaies', - bitcoin: 'Bitcoin', - ethereum: 'Ethereum', - monero: 'Monero', - code: 'Code', - joined: 'Membre depuis', - by: 'par', - to: 'vers', - from: 'de', - transfer_community: 'transférer la communauté', - transfer_site: 'transférer le site', - are_you_sure: 'Êtes-vous sûr ?', - yes: 'oui', - no: 'non', - powered_by: 'Propulsé par', - landing_0: - 'Lemmy est un <1>aggrégateur de lien, similaire à reddit et conçu pour fonctionner sur le <2>fédiverse.<3>Il est auto-hébergeable, se met à jour en direct et est léger (<4>~80kB). La fédération via Activitypub est prévue. <5>Lemmy est une <6>version beta très précoce, et de nombreuses fonctionnalités sont manquantes ou non fonctionnelles. <7>Vous pouvez rapporter des bugs et suggérez de nouvelles fonctionnalités <8>ici.<9>Crée avec <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.', - not_logged_in: "Vous n'êtes pas connecté.", - logged_in: 'Vous êtes connecté.', - community_ban: 'Vous avez été banni de cette communauté.', - site_ban: 'Vous avez été banni du site', - couldnt_create_comment: 'Impossible de poster le commentaire.', - couldnt_like_comment: "Impossible d'aimer le commentaire.", - couldnt_update_comment: 'Impossible de mettre à jour le commentaire.', - couldnt_save_comment: 'Impossible de sauvegarder le commentaire.', - couldnt_get_comments: 'Impossible de obtenir les commentaires.', - no_comment_edit_allowed: - "Vous n'êtes pas autorisé à éditer ce commentaire.", - no_post_edit_allowed: "Vous n'êtes pas autorisé à éditer sujet.", - no_community_edit_allowed: - "Vous n'êtes pas autorisé à éditer cette communauté.", - couldnt_find_community: 'Impossible de trouver cette communauté.', - couldnt_update_community: "Impossible d'éditer cette communauté.", - community_already_exists: 'Cette communauté existe déjà.', - community_moderator_already_exists: 'Ce membre est déjà modérateur.', - community_follower_already_exists: 'Ce membre est déjà abonné.', - community_user_already_banned: 'Ce membre est déjà banni.', - couldnt_create_post: 'Impossible de créer le sujet.', - post_title_too_long: 'Sujet titre trop long.', - couldnt_like_post: "Impossible d'aimer le sujet.", - couldnt_find_post: 'Impossible de trouver le sujet.', - couldnt_get_posts: "Impossible d'obtenir les sujets", - couldnt_update_post: 'Impossible de mettre à jour le sujet', - couldnt_save_post: 'Impossible de sauvegarder le sujet.', - no_slurs: "Pas d'insultes.", - not_an_admin: 'Pas administrateur.', - site_already_exists: 'Le site existe déjà.', - couldnt_update_site: 'Impossible de mettre à jour le site.', - couldnt_find_that_username_or_email: - 'Impossible de trouver cet utilisateur ou cet email.', - password_incorrect: 'Mot de passe incorrect.', - passwords_dont_match: 'Les mots de passes ne correspondent pas..', - admin_already_created: 'Désolé, il y a déjà un admin.', - user_already_exists: "L'utilisateur existe déjà.", - email_already_exists: "L'email existe déjà", - couldnt_update_user: "Impossible de mettre à jour l'utilisateur.", - system_err_login: - 'Erreur système. Essayez de vous déconneter puis de vous reconnecter.', - couldnt_create_private_message: 'Impossible de créer un message privé.', - no_private_message_edit_allowed: 'Pas autorisé à modifier un message privé.', - couldnt_update_private_message: 'Impossible de modifier un message privé.', - time: 'Temps', - action: 'Action', - }, -}; diff --git a/ui/src/translations/it.ts b/ui/src/translations/it.ts deleted file mode 100644 index 53d1894df..000000000 --- a/ui/src/translations/it.ts +++ /dev/null @@ -1,192 +0,0 @@ -export const it = { - translation: { - post: 'post', - remove_post: 'Rimuovi Post', - no_posts: 'Nessun Post.', - create_a_post: 'Crea un post', - create_post: 'Crea Post', - number_of_posts: '{{count}} Posts', - posts: 'Posts', - related_posts: 'Questi post potrebbero essere correlati', - cross_posts: 'Questo link è stato postato anche in:', - cross_post: 'cross-post', - comments: 'Commenti', - number_of_comments: '{{count}} Commenti', - remove_comment: 'Rimuovi Commento', - communities: 'Comunità', - users: 'Utenti', - create_a_community: 'Crea una Comunità', - create_community: 'Crea Comunità', - remove_community: 'Rimuovi Comunità', - subscribed_to_communities: 'Iscritto alle <1>comunità', - trending_communities: '<1>Comunità in crescita', - list_of_communities: 'Lista di comunità', - number_of_communities: '{{count}} Comunità', - community_reqs: 'minuscole, trattini bassi e nessuno spazio.', - edit: 'modifica', - reply: 'rispondi', - cancel: 'Annulla', - preview: 'Anteprima', - upload_image: 'carica immagine', - formatting_help: 'aiuto formattazione', - view_source: 'visualizza sorgente', - unlock: 'sblocca', - lock: 'blocca', - sticky: 'evidenzia', - unsticky: 'rimuovi evidenza', - link: 'link', - mod: 'moderatore', - mods: 'moderatori', - moderates: 'Moderatore di', - settings: 'Impostazioni', - remove_as_mod: 'rimuovi come moderatore', - appoint_as_mod: 'nomina come moderatore', - modlog: 'Registro di moderazione', - admin: 'amministratore', - admins: 'amministratori', - remove_as_admin: 'rimuovi come amministratore', - appoint_as_admin: 'nomina come amministratore', - remove: 'rimuovi', - removed: 'rimosso', - locked: 'bloccato', - stickied: 'evidenziato', - reason: 'Ragione', - mark_as_read: 'segna come letto', - mark_as_unread: 'segna come non letto', - delete: 'cancella', - deleted: 'cancellato', - delete_account: 'Cancella Account', - delete_account_confirm: 'Attenzione: stai per cancellare permanentemente tutti i tuoi dati. Sei sicuro?', - restore: 'ripristina', - ban: 'ban', - ban_from_site: 'banna dal sito', - unban: 'rimuovi ban', - unban_from_site: 'rimuove il ban dal sito', - banned: 'bannato', - save: 'salva', - unsave: 'rimuovi', - create: 'crea', - creator: 'autore', - username: 'Username', - email_or_username: 'Email o Username', - number_of_users: '{{count}} Utenti', - number_of_subscribers: '{{count}} Iscritti', - number_of_points: '{{count}} Punti', - number_online: '{{count}} Utenti Online', - name: 'Nome', - title: 'Titolo', - category: 'Categoria', - subscribers: 'Iscritti', - both: 'Entrambi', - saved: 'Salvato', - unsubscribe: 'Disiscriviti', - subscribe: 'Iscriviti', - subscribed: 'Iscritto', - prev: 'Precedente', - next: 'Prossima', - sidebar: 'Barra laterale', - sort_type: 'Ordina per', - hot: 'Popolari', - new: 'Nuovi', - top_day: 'Migliori della giornata', - week: 'Settimana', - month: 'Mese', - year: 'Anno', - all: 'Tutti', - top: 'Migliori', - api: 'API', - inbox: 'Posta in arrivo', - inbox_for: 'Posta di <1>{{user}}', - mark_all_as_read: 'segna tutti come letti', - type: 'Tipo', - unread: 'Non letti', - replies: 'Risposte', - mentions: 'Menzioni', - reply_sent: 'Risposta inviata', - search: 'Cerca', - overview: 'Panoramica', - view: 'Visualizza', - logout: 'Logout', - login_sign_up: 'Login / Iscriviti', - login: 'Login', - sign_up: 'Iscriviti', - notifications_error: 'Le notifiche desktop non sono supportate sul tuo browser. Prova Firefox o Chrome.', - unread_messages: 'Messaggi Non Letti', - password: 'Password', - verify_password: 'Verifica Password', - email: 'Email', - optional: 'Opzionale', - expires: 'Scade', - url: 'URL', - body: 'Contenuto', - copy_suggested_title: 'copia titolo suggerito: {{title}}', - community: 'Comunità', - expand_here: 'Visualizza qui', - subscribe_to_communities: 'Iscriviti ad una <1>comunità.', - chat: 'Chat', - recent_comments: 'Commenti Recenti', - no_results: 'Nessun risultato.', - setup: 'Setup', - lemmy_instance_setup: 'Setup dell\'istanza di Lemmy', - setup_admin: 'Imposta Amministratore del Sito', - your_site: 'il tuo sito', - modified: 'modificato', - nsfw: 'NSFW', - show_nsfw: 'Mostra contenuto NSFW', - theme: 'Tema', - sponsors: 'Sponsors', - sponsors_of_lemmy: 'Sponsors di Lemmy', - sponsor_message: 'Lemmy è un software gratuito e <1>open-source, il che significa nessuna pubblicità, monetizzazione o investitori esterni, per sempre. Le tue donazioni supportano direttamente lo sviluppo full-time del progetto. Si ringraziano le seguenti persone:', - support_on_patreon: 'Supporta su Patreon', - support_on_liberapay: 'Supporta su Liberapay', - general_sponsors: 'I "General Sponsors" sono quelli che hanno investito dai 10$ ai 39$ su Lemmy.', - crypto: 'Crypto', - bitcoin: 'Bitcoin', - ethereum: 'Ethereum', - monero: 'Monero', - code: 'Code', - joined: 'Iscritto da', - by: 'di', - to: 'su', - transfer_community: 'trasferisci comunità', - transfer_site: 'trasferisci sito', - are_you_sure: 'sei sicuro?', - yes: 'si', - no: 'no', - powered_by: 'Powered by', - landing_0: 'Lemmy è un <1>aggregatore di link / alternativa a reddit, creato per integrarsi con il <2>fediverse. <3>È self-hosted, i commenti sono aggiornati in tempo reale ed è molto piccolo (<4>~80kB). La Federazione con la rete ActivityPub sarà implementata nel futuro. <5>Questa versione è una <6>beta molto giovane e molte funzionalità sono incomplete o mancanti. <7>Suggerisci nuove funzionalità o segnala errori a <8>questa pagina.<9>Sviluppato con <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.', - not_logged_in: 'Non hai effettuato l\'accesso.', - community_ban: 'Sei stato bannato da questa comunità.', - site_ban: 'Sei stato bannato dal sito', - couldnt_create_comment: 'Impossibile creare il commento.', - couldnt_like_comment: 'Impossibile mettere \'Mi piace\' al commento.', - couldnt_update_comment: 'Impossibile aggiornare il commento.', - couldnt_save_comment: 'Impossibile salvare il commento.', - no_comment_edit_allowed: 'Non sei autorizzato a modificare il commento.', - no_post_edit_allowed: 'Non sei autorizzato a modificare il post.', - no_community_edit_allowed: 'Non sei autorizzato a modificare la comunità.', - couldnt_find_community: 'Impossibile trovare la comunità.', - couldnt_update_community: 'Impossibile aggiornare la comunità.', - community_already_exists: 'La comunità esiste già.', - community_moderator_already_exists: 'Questo utente è già moderatore della comunità.', - community_follower_already_exists: 'Questo utente è già moderatore della comunità.', - community_user_already_banned: 'L\'utente della comunità è già stato bannato.', - couldnt_create_post: 'Impossibile creare il post.', - couldnt_like_post: 'Impossibile mettere \'Mi piace\' post.', - couldnt_find_post: 'Impossibile trovare il post.', - couldnt_get_posts: 'Impossibile recuperare i post', - couldnt_update_post: 'Impossibile aggiornare il post', - couldnt_save_post: 'Impossibile salvare il post.', - no_slurs: 'Niente offese.', - not_an_admin: 'Non un amministratore.', - site_already_exists: 'Il sito esiste già.', - couldnt_update_site: 'Impossibile aggiornare il sito.', - couldnt_find_that_username_or_email: 'L\'username o la email non sono stati trovati.', - password_incorrect: 'Password non corretta.', - passwords_dont_match: 'Le password non corrispondono.', - admin_already_created: 'Spiacente, esiste già un amministratore.', - user_already_exists: 'L\'utente esiste già.', - couldnt_update_user: 'Impossibile aggiornare l\'utente.', - system_err_login: 'Si è verificato un errore. Prova ad effettuare nuovamente il login.', - }, -} diff --git a/ui/src/translations/nl.ts b/ui/src/translations/nl.ts deleted file mode 100644 index 4bf1fae5f..000000000 --- a/ui/src/translations/nl.ts +++ /dev/null @@ -1,234 +0,0 @@ -export const nl = { - translation: { - post: 'post', - remove_post: 'Verwijder post', - no_posts: 'Geen posts.', - create_a_post: 'Plaats een post', - create_post: 'Plaats post', - number_of_posts: '{{count}} posts', - posts: 'posts', - related_posts: 'Deze posts kunnen gerelateerd zijn', - cross_posts: 'Deze link is ook geplaatst in:', - cross_post: 'cross-post', - comments: 'Reacties', - number_of_comments: '{{count}} reacties', - remove_comment: 'Verwijder reactie', - communities: 'Communities', - users: 'Gebruikers', - create_a_community: 'Maak een community', - create_community: 'Maak community', - remove_community: 'Verwijder community', - subscribed_to_communities: 'Geabonneerd op <1>communities', - trending_communities: 'Populaire <1>communities', - list_of_communities: 'Lijst van communities', - number_of_communities: '{{count}} communities', - community_reqs: 'kleine letters, onderstrepingsteken en geen spaties', - edit: 'bewerk', - reply: 'reageer', - cancel: 'Annuleer', - unlock: 'ontsluiten', - lock: 'sluiten', - link: 'link', - mod: 'moderator', - mods: 'moderators', - moderates: 'Modereert', - settings: 'Instellingen', - remove_as_mod: 'Verwijder als moderator', - appoint_as_mod: 'Benoemen tot moderator', - modlog: 'Moderatorlog', - admin: 'beheerder', - admins: 'beheerders', - remove_as_admin: 'verwijder als beheerder', - appoint_as_admin: 'benoemen tot beheerder', - remove: 'weghalen', - removed: 'weggehaald', - locked: 'gesloten', - reason: 'Reden', - mark_as_read: 'markeer als gelezen', - mark_as_unread: 'markeer als ongelezen', - delete: 'verwijder', - deleted: 'verwijderd', - restore: 'herstellen', - ban: 'verban', - ban_from_site: 'verban van site', - unban: 'verbanning opzeggen', - unban_from_site: 'verbanning van site opzeggen', - save: 'opslaan', - unsave: 'unsave', - create: 'maak', - username: 'Gebruikersnaam', - email_or_username: 'E-mail of gebruikersnaam', - number_of_users: '{{count}} gebruikers', - number_of_subscribers: '{{count}} abonnees', - number_of_points: '{{count}} punten', - name: 'Naam', - title: 'Titel', - category: 'Categorie', - subscribers: 'Abonnees', - both: 'Beide', - saved: 'Opgeslagen', - unsubscribe: 'Afmelden', - subscribe: 'Abonneren', - subscribed: 'Geabonneerd', - prev: 'Vorige', - next: 'Volgende', - sidebar: 'Zijbalk', - sort_type: 'Sorteertype', - hot: 'Populair', - new: 'Nieuw', - top_day: 'Dagelijkse top', - week: 'Week', - month: 'Maand', - year: 'Jaar', - all: 'Alle', - top: 'Top', - api: 'API', - inbox: 'Postvak-in', - inbox_for: 'Postvak-in voor <1>{{user}}', - mark_all_as_read: 'markeer alle als gelezen', - type: 'Type', - unread: 'Ongelezen', - reply_sent: 'Reactie gestuurd', - search: 'Zoek', - overview: 'Overzicht', - view: 'Beeld', - logout: 'Log uit', - login_sign_up: 'Log in / Aanmelden', - login: 'Log in', - sign_up: 'Aanmelden', - notifications_error: - 'Bureabladberichten niet beschikbaar in je browser. Probeer Firefox of Chrome.', - unread_messages: 'Ongelezen berichten', - password: 'Wachtwoord', - verify_password: 'Herhaal wachtwoord', - email: 'E-mail', - optional: 'Optioneel', - expires: 'Verloopt', - url: 'url', - body: 'Tekst', - copy_suggested_title: 'neem voorgestelde titel over: {{title}}', - community: 'Community', - expand_here: 'Breid hier uit', - subscribe_to_communities: 'Abonneer je op een paar <1>communities.', - chat: 'Praat', - recent_comments: 'Recente reacties', - no_results: 'Geen resultaten', - setup: 'Installatie', - lemmy_instance_setup: 'Installatie van Lemmy-instantie', - setup_admin: 'Maak een administrator', - your_site: 'jouw site', - modified: 'bewerkt', - nsfw: 'NSFW', - show_nsfw: 'Laat NSFW-inhoud zien', - sponsors: 'Sponsoren', - sponsors_of_lemmy: 'Sponsoren van Lemmy', - sponsor_message: - 'Lemmy is vrije, <1>open-source software, dus zonder reclame, winstoogmerk en durfkapitaal, punt. Jouw donaties gaan direct naar de full-time-ontwikkeling van het project. Met veel dank aan de volgende mensen:', - support_on_patreon: 'Ondersteun op Patreon', - support_on_liberapay: 'Ondersteun op Liberapay', - general_sponsors: - 'Algemene sponsors zijn sponsors die tussen de $10 en $39 hebben gegeven aan Lemmy.', - crypto: 'Cryptovaluta', - bitcoin: 'Bitcoin', - ethereum: 'Ethereum', - monero: 'Monero', - code: 'Code', - joined: 'toegetreden', - by: 'door', - to: 'aan', - transfer_community: 'community overplaatsen', - transfer_site: 'site overplaatsen', - are_you_sure: 'weet je het zeker?', - yes: 'ja', - no: 'nee', - powered_by: 'Mogelijk gemaakt door', - landing_0: - 'Lemmy is een <1>linkverzameler / reddit-alternatief, bedoeld om in de <2>fediverse te werken.<3>Lemmy kan door om het even wie gehost worden, heeft live-bijgewerkte reacties en is superklein (<4>ca. 80 kB). Federatie in hte ActivityPub-netwerk is gepland. <5>Dit is een <6>erg vroege bèta-versie, en een hoop functies zijn stuk of afwezig. <7>Stel nieuwe functies voor of meldt fouten <8>hier.<9>Gemaakt met <10>Rust, <11>Actix, <12>Inferno en <13>Typescript.', - not_logged_in: 'Niet ingelogd.', - community_ban: 'Je bent verbannen uit deze community.', - site_ban: 'Je bent verbannen van deze site.', - couldnt_create_comment: 'Kon reactie niet maken.', - couldnt_like_comment: 'Kon reactie niet leuk vinden.', - couldnt_update_comment: 'Kon reactie niet bijwerken.', - couldnt_save_comment: 'Kon reactie niet opslaan.', - no_comment_edit_allowed: 'Niet toegestaan om reactie te bewerken.', - no_post_edit_allowed: 'Niet toegestaan om posts te bewerken.', - no_community_edit_allowed: 'Niet toegestaan om community te bewerken.', - couldnt_find_community: 'Kon community niet vinden.', - couldnt_update_community: 'Kon community niet bijwerken.', - community_already_exists: 'Community bestaat al.', - community_moderator_already_exists: 'Community-moderator bestaat al.', - community_follower_already_exists: 'Community-volger bestaat al.', - community_user_already_banned: 'Community-gebruiker reeds verbannen.', - couldnt_create_post: 'Kon post niet maken.', - couldnt_like_post: 'Kon post niet leuk vinden.', - couldnt_find_post: 'Kon post niet vinden.', - couldnt_get_posts: 'Kon posts niet ophalen.', - couldnt_update_post: 'Kon post niet bijwerken.', - couldnt_save_post: 'Kon post niet opslaan.', - no_slurs: 'Geen beledigingen.', - not_an_admin: 'Niet een beheerder.', - site_already_exists: 'Site bestaat al.', - couldnt_update_site: 'Kon site niet bijwerken.', - couldnt_find_that_username_or_email: - 'Kon gebruikersnaam of e-mailadres niet vinden.', - password_incorrect: 'Wachtwoord incorrect.', - passwords_dont_match: 'Wachtwoorden zijn niet gelijk.', - admin_already_created: 'Sorry, er is al een beheerder.', - user_already_exists: 'Gebruiker bestaat al.', - couldnt_update_user: 'Kon gebruiker niet bijwerken.', - system_err_login: - 'Systeemfout. Probeer uit te loggen en weer in te loggen.', - preview: 'voorbeeld', - upload_image: 'Afbeelding uploaden', - avatar: 'Avatar', - upload_avatar: 'Avatar uploaden', - show_avatars: 'Toon avatars', - formatting_help: 'Opmaak hulp', - view_source: 'bekijk bron', - sticky: 'vastplakken', - unsticky: 'loshalen', - archive_link: 'Archiveer link', - stickied: 'vastgeplakt', - delete_account: 'Verwijder account', - delete_account_confirm: 'Waarschuwing: dit zal al uw data voorgoed verwijderen, vul uw wachtwoord in om te bevestigen.', - banned: 'verbannen', - creator: 'auteur', - number_online: '{{count}} gebruikers online', - docs: 'Documentatie', - replies: 'Reacties', - mentions: 'vermeldingen', - old_password: 'Oud wachtwoord', - forgot_password: 'wachtwoord vergeten', - reset_password_mail_sent: 'Stuur een email om uw wachtwoord te resetten', - password_change: 'Wachtwoord aanpassen', - new_password: 'Nieuw wachtwoord', - no_email_setup: 'Deze server heeft email niet correct opgezet', - send_notifications_to_email: 'Stuur meldingen naar je email', - language: 'Taal', - browser_default: 'Browser standaard', - downvotes_disabled: 'Downvotes geblokkeerd', - enable_downvotes: 'Downvotes toestaan', - open_registration: 'Open registratie', - registration_closed: 'Registratie gesloten', - enable_nsfw: 'NSFW toestaan', - theme: 'Thema', - create_private_message: 'Maak een beveiligd bericht', - send_secure_message: 'Verstuur beveiligd bericht', - send_message: 'Verstuur bericht', - message: 'Bericht', - old: 'Oud', - message_sent: 'Bericht verstuurd', - messages: 'Berichten', - matrix_user_id: 'Matrix gebruikers-id', - private_message_disclaimer: 'Waarschuwing: Privé berichten in Lemmy zijn niet beveiligd. Maak een account aan op <1>Riot.im om veilig te communiceren', - donate_to_lemmy: 'Doneer aan Lemmy', - donate: 'Doneer', - from: 'van', - logged_in: 'Ingelogd', - email_already_exists: 'Email bestaat al', - couldnt_create_private_message: 'Kan beveiligd bericht niet maken', - no_private_message_edit_allowed: 'Niet toegestaan om privé berichten te wijzigen', - couldnt_update_private_message: 'Kan beveiligd bericht niet bijwerken' - }, -}; diff --git a/ui/src/translations/pt_br.ts b/ui/src/translations/pt_br.ts deleted file mode 100644 index aed781e87..000000000 --- a/ui/src/translations/pt_br.ts +++ /dev/null @@ -1,241 +0,0 @@ -export const pt_BR = { - translation: { - post: 'publicação', - remove_post: 'Apagar publicação', - no_posts: 'Sem publicações.', - create_a_post: 'Criar uma publicação', - create_post: 'Criar publicação', - number_of_posts: '{{count}} publicações', - posts: 'Publicações', - related_posts: 'Essas publicações podem estar relacionadas', - cross_posts: 'Esse link também foi publicado em:', - cross_post: 're-publicar', - cross_posted_to: 'Publicado também em: ', - comments: 'Comentários', - number_of_comments: '{{count}} comentários', - remove_comment: 'Apagar comentário', - communities: 'Comunidades', - users: 'Usuários', - create_a_community: 'Criar uma comunidade', - create_community: 'Criar comunidade', - remove_community: 'Apagar comunidade', - subscribed_to_communities: 'Inscrito em <1>comunidades', - trending_communities: '<1>Comunidades em tendência', - list_of_communities: 'Lista de comunidades', - number_of_communities: '{{count}} comunidades', - community_reqs: 'minúsculas, sublinhados e sem espaços.', - create_private_message: 'Criar mensagem privada', - send_secure_message: 'Enviar mensagem segura', - send_message: 'Enviar mensagem', - message: 'Mensagem', - edit: 'editar', - reply: 'responder', - cancel: 'Cancelar', - preview: 'Pré-visualização', - upload_image: 'fazer upload de imagem', - avatar: 'Avatar', - upload_avatar: 'Fazer upload de avatar', - show_avatars: 'Mostrar Avatars', - formatting_help: 'ajuda de formatação', - view_source: 'ver fonte', - unlock: 'desbloquear', - lock: 'bloquear', - sticky: 'fixar', - unsticky: 'desafixar', - link: 'link', - archive_link: 'arquivar link', - mod: 'moderador', - mods: 'moderadores', - moderates: 'Modera', - settings: 'Configurações', - remove_as_mod: 'remover como moderador', - appoint_as_mod: 'designar como moderador', - modlog: 'Registro de moderação', - admin: 'administrador', - admins: 'administradores', - remove_as_admin: 'remover como administrador', - appoint_as_admin: 'designar como administrador', - remove: 'remover', - removed: 'removido', - locked: 'trancado', - stickied: 'fixado', - reason: 'Motivo', - mark_as_read: 'marcar como lido', - mark_as_unread: 'marcar como não lido', - delete: 'apagar', - deleted: 'apagado', - delete_account: 'Apagar conta', - delete_account_confirm: - 'Aviso: isso vai apagar seus dados de forma permanente. Escreva sua senha para confirmar.', - restore: 'restaurar', - ban: 'banir', - ban_from_site: 'banido do site', - unban: 'readmitido', - unban_from_site: 'readmitido ao site', - banned: 'banido', - save: 'guardar', - unsave: 'descartar', - create: 'criar', - creator: 'criador', - username: 'nome de usuário', - email_or_username: 'E-mail ou nome de usuário', - number_of_users: '{{count}} usuários', - number_of_subscribers: '{{count}} inscritos', - number_of_points: '{{count}} pontos', - number_online: '{{count}} usuários online', - name: 'Nome', - title: 'Título', - category: 'Categoria', - subscribers: 'Inscritos', - both: 'Ambos', - saved: 'Guardado', - unsubscribe: 'Cancelar inscrição', - subscribe: 'Inscrever-se', - subscribed: 'Inscrito', - prev: 'Anterior', - next: 'Próximo', - sidebar: 'Barra lateral', - sort_type: 'Ordenação', - hot: 'Popular', - new: 'Novo', - old: 'Velho', - top_day: 'Top do dia', - week: 'Semana', - month: 'Mês', - year: 'Ano', - all: 'Tudo', - top: 'Top', - api: 'API', - docs: 'Docs', - inbox: 'Caixa de entrada', - inbox_for: 'Caixa de entrada de <1>{{user}}', - mark_all_as_read: 'marcar tudo como lido', - type: 'Tipo', - unread: 'Não lido', - replies: 'Respostas', - mentions: 'Menções', - reply_sent: 'Resposta enviada', - message_sent: 'Mensagem enviada', - search: 'Busca', - overview: 'Visão geral', - view: 'Visualização', - logout: 'Sair', - login_sign_up: 'Entrar / Inscrever-se', - login: 'Entrar', - sign_up: 'Inscrever-se', - notifications_error: - 'Seu navegador não oferece notificações para a área de trabalho. Tente o Firefox ou o Chrome.', - unread_messages: 'Mensagens não lidas', - messages: 'Mensagens', - password: 'Senha', - verify_password: 'Verifique a senha', - old_password: 'Senha antiga', - forgot_password: 'esqueci a senha', - reset_password_mail_sent: 'Enviado um e-mail para a alteração da senha.', - password_change: 'Alteração de senha', - new_password: 'Nova senha', - no_email_setup: 'Esse servidor não configurou corretamente o e-mail.', - email: 'E-mail', - matrix_user_id: 'Usuário Matrix', - private_message_disclaimer: - 'Aviso: mensagens privadas no Lemmy não são seguras. Crie uma conta em <1>Riot.im para troca segura de mensagens.', - send_notifications_to_email: 'Enviar notificações para o e-mail', - optional: 'Opcional', - expires: 'Expira', - language: 'Idioma', - browser_default: 'Padrão do navegador', - downvotes_disabled: 'Votos negativos desativados', - enable_downvotes: 'Permitir votos negativos', - open_registration: 'Permitir registro', - registration_closed: 'Registros desativados', - enable_nsfw: 'Permitir NSFW', - url: 'URL', - body: 'Conteúdo', - copy_suggested_title: 'copiar título sugerido: {{title}}', - community: 'Comunidade', - expand_here: 'Expandir aqui', - subscribe_to_communities: 'Inscreva-se em algumas <1>comunidades.', - chat: 'Chat', - recent_comments: 'Últimos comentários', - no_results: 'Nenhum resultado.', - setup: 'Instalação', - lemmy_instance_setup: 'Criação de instância Lemmy', - setup_admin: 'Configurar administrador do site', - your_site: 'seu site', - modified: 'modificado', - nsfw: 'NSFW', - show_nsfw: 'Mostrar conteúdo NSFW', - theme: 'Tema', - sponsors: 'Patrocinadores', - sponsors_of_lemmy: 'Patrocinadores do Lemmy', - sponsor_message: - 'Lemmy é um programa livre e de código aberto, o que significa que não haverá publicidade, monetização ou capital de risco, jamais. Suas doações apoiam de forma direta o desenvolvimento em tempo integral do projeto. Muitos agradecimentos às sequintes pessoas:', - support_on_patreon: 'Colabore no Patreon', - support_on_liberapay: 'Colabore no Liberapay', - donate_to_lemmy: 'Faça uma doação ao Lemmy', - donate: 'Doar', - general_sponsors: - 'Patrocinadores são aqueles que doaram entre $10 e $39 ao Lemmy.', - crypto: 'Crypto', - bitcoin: 'Bitcoin', - ethereum: 'Ethereum', - monero: 'Monero', - code: 'Code', - joined: 'Entrou', - by: 'por', - to: 'para', - from: 'de', - transfer_community: 'transferir comunidade', - transfer_site: 'transferir site', - are_you_sure: 'tem certeza?', - yes: 'sim', - no: 'não', - powered_by: 'Powered by', - landing_0: - 'Lemmy é um <1>agregador de links / alternativa ao reddit, com a intenção de funcionar junto ao <2>fediverso.<3>Pode ser hospedado em servidor próprio, tem atualização de comentários em tempo real e é minúsculo (<4>~80kB). A federação com a rede ActivityPub está no roteiro do projeto. <5>Esta é uma <6>versão beta bastante antecipada, e muitas funcionalidades ainda estão quebradas ou ausentes. <7>Sugira novas funcionalidades ou reporte erros <8>aqui.<9>Feito com <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.', - not_logged_in: 'Não autenticado.', - logged_in: 'Autenticado.', - community_ban: 'Você foi banido desta comunidade.', - site_ban: 'Você foi banido do site', - couldnt_create_comment: 'Não foi possível criar o comentário.', - couldnt_like_comment: 'Não foi possível curtir o comentário.', - couldnt_update_comment: 'Não foi possível atualizar o comentário.', - couldnt_save_comment: 'Não foi possível guardar o comentário.', - no_comment_edit_allowed: 'Sem permissão para editar de comentário.', - no_post_edit_allowed: 'Sem permissão para editar publicação.', - no_community_edit_allowed: 'Sem permissão para editar comunidade.', - couldnt_find_community: 'Não foi possível encontrar a comunidade.', - couldnt_update_community: 'Não foi possível atualizar a comunidade.', - community_already_exists: 'Esta comunidade já existe.', - community_moderator_already_exists: - 'Este moderador da comunidade já existe.', - community_follower_already_exists: 'Este seguidor da comunidade já existe.', - community_user_already_banned: 'Este usuário da comunidade já foi banido.', - couldnt_create_post: 'Não foi possível criar a publicação.', - couldnt_like_post: 'Não foi possível curtir a publicação.', - couldnt_find_post: 'Não foi possível encontrar a publicação.', - couldnt_get_posts: 'Não foi possível obter as publicações', - couldnt_update_post: 'Não foi possível atualizar a publicação', - couldnt_save_post: 'Não foi possível guardar a publicação.', - no_slurs: 'Sem insultos.', - not_an_admin: 'Não é administrador.', - site_already_exists: 'O site já existe.', - couldnt_update_site: 'Não foi possível atualizar o site.', - couldnt_find_that_username_or_email: - 'Não foi possível encontrar esse usuário ou e-mail.', - password_incorrect: 'Senha incorreta.', - passwords_dont_match: 'As senhas não são iguais.', - admin_already_created: 'Desculpe, já há um administrador.', - user_already_exists: 'Este usuário já existe.', - email_already_exists: 'Este e-mail já existe.', - couldnt_update_user: 'Não foi possível atualizar o usuário.', - system_err_login: 'Erro no sistema. Tente sair e autenticar-se outra vez.', - couldnt_create_private_message: 'Não foi possível criar mensagem privada.', - no_private_message_edit_allowed: - 'Sem permissão para editar mensagem privada.', - couldnt_update_private_message: - 'Não foi possível atualizar a mensagem privada.', - time: 'Tempo', - action: 'Ação', - }, -}; diff --git a/ui/src/translations/ru.ts b/ui/src/translations/ru.ts deleted file mode 100644 index e8ae73619..000000000 --- a/ui/src/translations/ru.ts +++ /dev/null @@ -1,170 +0,0 @@ -export const ru = { - translation: { - post: 'запись', - remove_post: 'Удалить запись', - no_posts: 'Нет записей.', - create_a_post: 'Создать запись', - create_post: 'Создать запись', - number_of_posts: '{{count}} записей', - posts: 'Записи', - related_posts: 'Эти записи могут быть связаны', - comments: 'Комментарии', - number_of_comments: '{{count}} комментариев', - remove_comment: 'Удалить комментарий', - communities: 'Сообщества', - users: 'Пользователи', - create_a_community: 'Создать сообщество', - create_community: 'Создать сообщество', - remove_community: 'Удалить сообщество', - subscribed_to_communities: 'Подписаны на <1>сообщества', - trending_communities: '<1>Сообщества в тренде', - list_of_communities: 'Список сообществ', - community_reqs: 'строчными буквами, подчеркиваниями и без пробелов.', - edit: 'редактировать', - reply: 'ответить', - cancel: 'Отмена', - unlock: 'разблокировать', - lock: 'заблокировать', - link: 'ссылка', - mod: 'модератор', - mods: 'модераторы', - moderates: 'Модерация', - settings: 'Настройки', - remove_as_mod: 'снять из модераторов', - appoint_as_mod: 'назначить модератором', - modlog: 'Модлог', - admin: 'администратор', - admins: 'администраторы', - remove_as_admin: 'снять из администраторов', - appoint_as_admin: 'назначить администратором', - remove: 'убрать', - removed: 'убрано', - locked: 'заблокировано', - reason: 'Причина', - mark_as_read: 'пометить как прочитанное', - mark_as_unread: 'пометить как непрочитанное', - delete: 'удалить', - deleted: 'удалено', - restore: 'восстановить', - ban: 'заблокировать', - ban_from_site: 'заблокировать на сайте', - unban: 'разблокировать', - unban_from_site: 'разблокировать на сайте', - save: 'сохранить', - unsave: 'не сохранять', - create: 'создать', - username: 'Имя пользователя', - email_or_username: 'Электронная почта или имя пользователя', - number_of_users: '{{count}} пользователей', - number_of_subscribers: '{{count}} подписчиков', - number_of_points: '{{count}} баллов', - name: 'Имя', - title: 'Название', - category: 'Категория', - subscribers: 'Подписчики', - both: 'Оба', - saved: 'Сохранено', - unsubscribe: 'Отписаться', - subscribe: 'Подписаться', - subscribed: 'Подписаны', - prev: 'Назад', - next: 'Далее', - sidebar: 'Боковая панель', - sort_type: 'Тип сортировки', - hot: 'Популярно', - new: 'Новое', - top_day: 'Лучшее за день', - week: 'Неделя', - month: 'Месяц', - year: 'Год', - all: 'Всё', - top: 'Лучшее', - api: 'API', - inbox: 'Входящие', - inbox_for: 'Входящие сообщения для <1>{{user}}', - mark_all_as_read: 'пометить все как прочитанные', - type: 'Тип', - unread: 'Не прочитано', - reply_sent: 'Ответ отправлен', - search: 'Поиск', - overview: 'Обзор', - view: 'Просмотр', - logout: 'Выйти', - login_sign_up: 'Войти / Регистрация', - login: 'Авторизация', - sign_up: 'Регистрация', - notifications_error: - 'Уведомления на рабочем столе недоступны в вашем браузере. Попробуйте Firefox или Chrome.', - unread_messages: 'Непрочитанные сообщения', - password: 'Пароль', - verify_password: 'Повторите пароль', - email: 'Электронная почта', - optional: 'Необязательно', - expires: 'Истёк', - url: 'URL', - body: 'Тело', - copy_suggested_title: 'предложенное название: {{title}}', - community: 'Сообщество', - expand_here: 'Расширить здесь', - subscribe_to_communities: 'Подпишитесь на некоторые <1>сообщества.', - chat: 'Чат', - no_results: 'Нет результатов.', - setup: 'Установка', - lemmy_instance_setup: 'Установка инстанции Lemmy', - setup_admin: 'Настройка администратора сайта', - your_site: 'ваш сайт', - modified: 'изменено', - nsfw: 'NSFW', - show_nsfw: 'Показывать NSFW-контент', - sponsors: 'Спонсоры', - sponsors_of_lemmy: 'Спонсоры Lemmy', - sponsor_message: - 'Lemmy это бесплатное, <1>открытое программное обеспечение, что означает отсутствие рекламы, монетизации или венчурного капитала, никогда. Ваши пожертвования напрямую поддерживают развитие проекта. Спасибо нижеуказанным людям:', - support_on_patreon: 'Поддержать на Patreon', - general_sponsors: - 'Генеральные спонсоры - это те, кто пообещал Lemmy от $10 до $39.', - crypto: 'Крипто', - bitcoin: 'Bitcoin', - ethereum: 'Ethereum', - code: 'Код', - joined: 'Присоединился', - powered_by: 'Работает на', - landing_0: - 'Lemmy - это <1>агрегатор ссылок / альтернатива reddit, предназначенный для работы в <2>федиверсе.<3>Это самодостаточная система, с обновляемыми комментариями, и эта система крошечная (<4>~80 Кб). Федерация в сети ActivityPub находится в разработке. <5>Это <6>очень ранняя бета-версия, и многие функции в настоящее время сломаны или отсутствуют. <7>Предлагать новые функции или сообщать об ошибках можно <8>здесь.<9>Сделано на <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.', - 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: 'Сообщество уже существует.', - community_moderator_already_exists: 'Модератор сообщества уже существует.', - community_follower_already_exists: 'Подписчик сообщества уже существует.', - community_user_already_banned: 'Пользователь сообщества уже заблокирован.', - couldnt_create_post: 'Не получилось создать запись.', - couldnt_like_post: 'Не получилось лайкнуть запись.', - couldnt_find_post: 'Не получилось найти запись.', - couldnt_get_posts: 'Не получилось найти записи', - couldnt_update_post: 'Не получилось обновить запись', - couldnt_save_post: 'Не получилось сохранить запись.', - no_slurs: 'Без оскорблений.', - not_an_admin: 'Не администратор.', - site_already_exists: 'Сайт уже существует.', - couldnt_update_site: 'Не получилось обновить сайт.', - couldnt_find_that_username_or_email: - 'Не получилось найти данное имя пользователя или электронную почту.', - password_incorrect: 'Неверный пароль.', - passwords_dont_match: 'Пароли не совпадают.', - admin_already_created: 'Извините, уже есть администратор.', - user_already_exists: 'Пользователь уже существует.', - couldnt_update_user: 'Не получилось обновить пользователя.', - system_err_login: - 'Системная ошибка. Попробуйте выйти из системы и вернуться обратно.', - }, -}; diff --git a/ui/src/translations/sv.ts b/ui/src/translations/sv.ts deleted file mode 100644 index f0a4d79e7..000000000 --- a/ui/src/translations/sv.ts +++ /dev/null @@ -1,195 +0,0 @@ -export const sv = { - translation: { - post: 'inlägg', - remove_post: 'Radera inlägg', - no_posts: 'Inga inlägg.', - create_a_post: 'Skriv ett inlägg', - create_post: 'Skapa inlägg', - number_of_posts: '{{count}} inlägg', - posts: 'Inlägg', - related_posts: 'Dessa inlägg kan vara relaterade', - cross_posts: 'Den här länken har även publicerats i:', - cross_post: 'tvärinlägg', - comments: 'Kommentarer', - number_of_comments: '{{count}} kommentarer', - remove_comment: 'Radera kommentar', - communities: 'Gemenskaper', - users: 'Användare', - create_a_community: 'Skapa en gemenskap', - create_community: 'Skapa gemenskap', - remove_community: 'Radera gemenskap', - subscribed_to_communities: 'Prenumererar på <1>gemenskaper', - trending_communities: 'Populära <1>gemenskaper', - list_of_communities: 'Lista över gemenskaper', - number_of_communities: '{{count}} gemenskaper', - community_reqs: 'gemener, understreck och inga blanksteg.', - edit: 'redigera', - reply: 'svara', - cancel: 'Avbryt', - preview: 'Förhandsgranskning', - upload_image: 'ladda upp bild', - formatting_help: 'formateringshjälp', - view_source: 'visa källkod', - unlock: 'lås upp', - lock: 'lås', - sticky: 'fastnålad', - unsticky: 'inte fastnålad', - link: 'länk', - mod: 'moderator', - mods: 'moderatorer', - moderates: 'Modererar', - settings: 'Inställningar', - remove_as_mod: 'tag bort som moderator', - appoint_as_mod: 'lägg till som moderator', - modlog: 'Moderationslogg', - admin: 'administratör', - admins: 'administratörer', - remove_as_admin: 'tag bort som administratör', - appoint_as_admin: 'lägg till som administratör', - remove: 'ta bort', - removed: 'borttagen', - locked: 'låst', - stickied: 'fastnålad', - reason: 'Anledning', - mark_as_read: 'markera som läst', - mark_as_unread: 'markera som oläst', - delete: 'radera', - deleted: 'raderad', - delete_account: 'Ta bort konto', - delete_account_confirm: - 'Varning: den här åtgärden kommer radera alla dina data permanent. Är du säker?', - restore: 'återställ', - ban: 'blockera', - ban_from_site: 'blockera från webbplats', - unban: 'ta bort blockering', - unban_from_site: 'ta bort blockering från webbplats', - banned: 'blocerad', - save: 'spara', - unsave: 'förkasta', - create: 'skapa', - creator: 'skapare', - username: 'Användarnamn', - email_or_username: 'E-postadress eller användarnamn', - number_of_users: '{{count}} användare', - number_of_subscribers: '{{count}} prenumeranter', - number_of_points: '{{count}} poäng', - number_online: '{{count}} användare inloggade', - name: 'Namn', - title: 'Titel', - category: 'Kategori', - subscribers: 'Prenumeranter', - both: 'Båda', - saved: 'Sparade', - unsubscribe: 'Avbryt prenumeration', - subscribe: 'Prenumerera', - subscribed: 'Prenumererar', - prev: 'Föregående', - next: 'Nästa', - sidebar: 'Sidlist', - sort_type: 'Sorteringstyp', - hot: 'Hett', - new: 'Nytt', - top_day: 'Dagstoppen', - week: 'Vecka', - month: 'Månad', - year: 'År', - all: 'Samtliga', - top: 'Topp', - api: 'API', - inbox: 'Inkorg', - inbox_for: 'Inkorg tillhörande <1>{{user}}', - mark_all_as_read: 'markera alla som lästa', - type: 'Typ', - unread: 'Oläst', - reply_sent: 'Svar skickat', - search: 'Sök', - overview: 'Översikt', - view: 'Vy', - logout: 'Logga ut', - login_sign_up: 'Logga in eller skapa konto', - login: 'Logga in', - sign_up: 'Skapa konto', - notifications_error: - 'Din webbläsare har inte stöd för skrivbordsaviseringar. Testa Firefox eller Chrome.', - unread_messages: 'Olästa meddelanden', - password: 'Lösenord', - verify_password: 'Bekräfta lösenord', - email: 'E-postadress', - optional: 'Valfritt', - expires: 'Går ut', - url: 'URL', - body: 'Text', - copy_suggested_title: 'kopiera föreslagen titel: {{title}}', - community: 'Gemenskap', - expand_here: 'Utvidga här', - subscribe_to_communities: 'Prenumerera på några <1>gemenskaper.', - chat: 'Chatta', - recent_comments: 'Senaste kommentarer', - no_results: 'Inga resultat.', - setup: 'Installering', - lemmy_instance_setup: 'Installering av Lemmy-instans', - setup_admin: 'Skapa en administratör', - your_site: 'din webbplats', - modified: 'ändrades', - nsfw: 'Känsligt eller oförbehållsamt innehåll', - show_nsfw: 'Visa känsligt eller oförbehållsamt innehåll', - theme: 'Utseende', - sponsors: 'Sponsorer', - sponsors_of_lemmy: 'Lemmys sponsorer', - sponsor_message: - 'Lemmy är fri mjukvara med <1>öppen källkod, vilket innebär att ingen reklam, vinstindrivning eller venturekapital förekommer, någonsin. Dina donationer går direkt till att stöda utvecklingen av projektet. Stort tack till följande personer:', - support_on_patreon: 'Stöd på Patreon', - general_sponsors: - 'Allmänna sponsorer är dem som givit mellan 10 och 39\u00a0dollar till Lemmy.', - crypto: 'Kryptovaluta', - bitcoin: 'Bitcoin', - ethereum: 'Ethereum', - monero: 'Monero', - code: 'Kod', - joined: 'Gick med', - by: 'av', - to: 'till', - transfer_community: 'flytta gemenskap', - transfer_site: 'flytta webbplats', - are_you_sure: 'är du säker?', - yes: 'ja', - no: 'nej', - powered_by: 'Drivs av', - landing_0: - 'Lemmy är en <1>länksamlare och alternativ till reddit, ämnad att fungera i <2>Fediversumet.<3>Lemmy kan drivas av vem som helst, har kommentarstrådar som updateras i realid och är mycket liten (<4>ca 80\u00a0kB). Federering med ActivityPub-nätverket är planerat. <5>Detta är en <6>väldigt tidig betaversion och många funktioner saknas därför eller är trasiga.<7>Föreslå nya funktioner eller anmäl buggar <8>här.<9>Skapad i <10>Rust, <11>Actix, <12>Inferno och <13>Typescript.', - not_logged_in: 'Inte inloggad.', - community_ban: 'Du har blockerats från den här gemenskapen.', - site_ban: 'Du har blockerats från webbplatsen.', - couldnt_create_comment: 'Kunde inte skapa kommentar.', - couldnt_like_comment: 'Kunde inte gilla kommentar.', - couldnt_update_comment: 'Kunde inte uppdatera kommentar.', - couldnt_save_comment: 'Kunde inte spara kommentar.', - no_comment_edit_allowed: 'Har inte behörighet att redigera komentar.', - no_post_edit_allowed: 'Har inte behörighet att redigera inlägg.', - no_community_edit_allowed: 'Har inte behörighet att redigera gemenskap.', - couldnt_find_community: 'Kunde inte hitta gemenskap.', - couldnt_update_community: 'Kunde inte uppdatera gemenskap.', - community_already_exists: 'Gemenskapen finns redan.', - community_moderator_already_exists: 'Gemenskapsmoderatorn finns redan.', - community_follower_already_exists: 'Gemenskapsföljaren finns redan.', - community_user_already_banned: 'Gemenskapsanvändaren redan blockerad.', - couldnt_create_post: 'Kunde inte skapa inlägg.', - couldnt_like_post: 'Kunde inte gilla inlägg.', - couldnt_find_post: 'Kunde inte hitta inlägg.', - couldnt_get_posts: 'Kunde inte hämta inlägg.', - couldnt_update_post: 'Kunde inte uppdatera inlägg.', - couldnt_save_post: 'Kunde inte spara inlägg.', - no_slurs: 'Inga förolämpningar.', - not_an_admin: 'Inte en administratör.', - site_already_exists: 'Webbplatsen finns redan.', - couldnt_update_site: 'Kunde inte uppdatera webbplats.', - couldnt_find_that_username_or_email: - 'Kunde inte hitta det användarnamnet eller e-postadressen.', - password_incorrect: 'Ogiltigt lösenord.', - passwords_dont_match: 'Lösenorden stämmer inte överens.', - admin_already_created: 'Beklagar, men det finns redan en administratör.', - user_already_exists: 'Användaren finns redan.', - couldnt_update_user: 'Kunde inte uppdatera användare.', - system_err_login: 'Systemfel. Försök att logga ut och sedan in igen.', - }, -}; diff --git a/ui/src/translations/zh.ts b/ui/src/translations/zh.ts deleted file mode 100644 index e4111996d..000000000 --- a/ui/src/translations/zh.ts +++ /dev/null @@ -1,164 +0,0 @@ -export const zh = { - translation: { - post: '帖子', - remove_post: '移除帖子', - no_posts: '没有帖子.', - create_a_post: '创建新帖子', - create_post: '创建帖子', - number_of_posts: '{{count}} 帖子', - posts: '帖子', - related_posts: '相关的帖子', - comments: '评论', - number_of_comments: '{{count}} 评论', - remove_comment: '移除评论', - communities: '节点', - create_a_community: '创建新节点', - create_community: '创建节点', - remove_community: '移除节点', - subscribed_to_communities: '订阅新 <1>节点', - trending_communities: '<1>节点趋势', - list_of_communities: '节点列表', - community_reqs: '包含小写与下划线且没有空格的字符串.', - edit: '编辑', - reply: '回应', - cancel: '取消', - unlock: '解锁', - lock: '加锁', - link: '链接', - mod: '监管人', - mods: '监管人', - moderates: '监管', - remove_as_mod: '添加监管人', - appoint_as_mod: '移除监管人', - modlog: '监管记录', - admin: '管理权限', - admins: '管理权限', - remove_as_admin: '移除管理权限', - appoint_as_admin: '添加管理权限', - remove: '移除', - removed: '已移除', - locked: '已加锁', - reason: '原因', - mark_as_read: '标记未读', - mark_as_unread: '标记已读', - delete: '删除', - deleted: '已删除', - restore: '恢复', - ban: '禁止', - ban_from_site: '禁止此站点', - unban: '取消', - unban_from_site: '取消禁止', - save: '保存', - unsave: '取消保存', - create: '创建', - username: '用户名', - email_or_username: '邮箱或用户名', - number_of_users: '{{count}} 用户', - number_of_subscribers: '{{count}} 订阅', - number_of_points: '{{count}} 分', - name: '名字', - title: '标题', - category: '分类', - subscribers: '订阅', - both: '全部', - saved: '保存', - unsubscribe: '取消订阅', - subscribe: '订阅', - subscribed: '已订阅', - prev: '上一页', - next: '下一页', - sidebar: '侧边栏', - sort_type: '排序方式', - hot: '最热', - new: '最新', - top_day: '今日', - week: '周', - month: '月', - year: '年', - all: '所有', - top: '最热', - api: '应用程式介面', - inbox: '收件箱', - inbox_for: '<1>{{user}} 收件箱', - mark_all_as_read: '标记所有已读', - type: '类型', - unread: '未读', - reply_sent: '回复发送', - search: '搜索', - overview: '个人中心', - view: '查看', - logout: '注销', - login_sign_up: '登录/注册', - login: '登录', - sign_up: '注册', - notifications_error: '你的浏览器不支持桌面通知,尝试 Firefox 或 Chrome', - unread_messages: '未读消息', - password: '密码', - verify_password: '确认密码', - email: '邮箱', - optional: '选项', - expires: '过期', - url: '网址', - body: '内容', - copy_suggested_title: '复制建议的标题: {{title}}', - community: '节点', - expand_here: '展开', - subscribe_to_communities: '订阅一些 <1>节点.', - chat: '聊天', - no_results: '没有结果.', - setup: '设置', - lemmy_instance_setup: 'Lemmy Instance Setup', - setup_admin: '设置管理员', - your_site: '你的站点', - modified: '修改', - 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: '在 Patreon 赞助', - support_on_liberapay: '在 on 赞助', - general_sponsors: - 'General Sponsors are those that pledged $10 to $39 to Lemmy.', - crypto: '加密', - bitcoin: '比特币', - ethereum: '以太币', - code: '代码', - joined: '已加入', - powered_by: '保留所有权利', - landing_0: - "Lemmy is a <1>link aggregator / reddit alternative, intended to work in the <2>fediverse.<3>It's self-hostable, has live-updating comment threads, and is tiny (<4>~80kB). Federation into the ActivityPub network is on the roadmap. <5>This is a <6>very early beta version, and a lot of features are currently broken or missing. <7>Suggest new features or report bugs <8>here.<9>Made with <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.", - 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: '节点已存在.', - community_moderator_already_exists: '节点监管人已存在.', - community_follower_already_exists: '节点追随者已存在.', - community_user_already_banned: '节点用户已禁止.', - couldnt_create_post: '不能创建帖子.', - couldnt_like_post: '不能收藏帖子.', - couldnt_find_post: '不能搜寻帖子.', - couldnt_get_posts: '不能获取帖子', - couldnt_update_post: '不能更新帖子', - couldnt_save_post: '不能保持帖子.', - no_slurs: '和谐.', - not_an_admin: '不是管理员.', - site_already_exists: '站点已存在.', - couldnt_update_site: '不能更新站点.', - couldnt_find_that_username_or_email: '用户名/邮箱不存在.', - password_incorrect: '密码不正确.', - passwords_dont_match: '密码不匹配.', - admin_already_created: '抱歉,管理员已存在.', - user_already_exists: '用户已存在.', - couldnt_update_user: '不可以更新用户.', - system_err_login: '系统错误. 尝试注销再登录', - }, -}; From 51653015a075e23bbc26acec198ede785b857602 Mon Sep 17 00:00:00 2001 From: Felix Date: Fri, 28 Feb 2020 18:48:30 +0100 Subject: [PATCH 048/164] Make i18n compatible with weblate (ref #387) --- ui/assets/translations/fi.json | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/assets/translations/fi.json b/ui/assets/translations/fi.json index c766cacd0..feeb269b9 100644 --- a/ui/assets/translations/fi.json +++ b/ui/assets/translations/fi.json @@ -1,5 +1,4 @@ { - "translation": { "post": "viesti", "remove_post": "Poista viesti", "no_posts": "Ei viestjä.", From 8ed1610f48dd6bf0f2a5ba5d93c8766572bb68bb Mon Sep 17 00:00:00 2001 From: Dessalines Date: Sat, 29 Feb 2020 13:10:28 -0500 Subject: [PATCH 049/164] Version v0.6.26 --- ansible/VERSION | 2 +- docker/prod/docker-compose.yml | 2 +- server/src/version.rs | 2 +- ui/src/version.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ansible/VERSION b/ansible/VERSION index 6095ec4eb..f37037aee 100644 --- a/ansible/VERSION +++ b/ansible/VERSION @@ -1 +1 @@ -v0.6.25 +v0.6.26 diff --git a/docker/prod/docker-compose.yml b/docker/prod/docker-compose.yml index ac46ece30..13fa5508c 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.25 + image: dessalines/lemmy:v0.6.26 ports: - "127.0.0.1:8536:8536" restart: always diff --git a/server/src/version.rs b/server/src/version.rs index e504bd466..81456d0b2 100644 --- a/server/src/version.rs +++ b/server/src/version.rs @@ -1 +1 @@ -pub const VERSION: &str = "v0.6.25"; +pub const VERSION: &str = "v0.6.26"; diff --git a/ui/src/version.ts b/ui/src/version.ts index 78b06fe35..8eb5e2cd8 100644 --- a/ui/src/version.ts +++ b/ui/src/version.ts @@ -1 +1 @@ -export const version: string = 'v0.6.25'; +export const version: string = 'v0.6.26'; From d7f49cea9a809f9c24947e7c4c941118c22a1f86 Mon Sep 17 00:00:00 2001 From: Felix Date: Fri, 28 Feb 2020 22:18:47 +0100 Subject: [PATCH 050/164] generate typescript during compilation --- .gitignore | 1 + ui/generate_translations.js | 25 ++++++++++++ ui/package.json | 4 +- ui/src/i18next.ts | 53 +++++++++++++++++++------ ui/translation_report.ts | 4 +- ui/{assets => }/translations/ca.json | 0 ui/{assets => }/translations/de.json | 0 ui/{assets => }/translations/en.json | 0 ui/{assets => }/translations/eo.json | 0 ui/{assets => }/translations/es.json | 0 ui/{assets => }/translations/fa.json | 0 ui/{assets => }/translations/fi.json | 0 ui/{assets => }/translations/fr.json | 0 ui/{assets => }/translations/it.json | 0 ui/{assets => }/translations/nl.json | 0 ui/{assets => }/translations/pt_br.json | 0 ui/{assets => }/translations/ru.json | 0 ui/{assets => }/translations/sv.json | 0 ui/{assets => }/translations/zh.json | 0 ui/yarn.lock | 8 ++-- 20 files changed, 74 insertions(+), 21 deletions(-) create mode 100644 ui/generate_translations.js rename ui/{assets => }/translations/ca.json (100%) rename ui/{assets => }/translations/de.json (100%) rename ui/{assets => }/translations/en.json (100%) rename ui/{assets => }/translations/eo.json (100%) rename ui/{assets => }/translations/es.json (100%) rename ui/{assets => }/translations/fa.json (100%) rename ui/{assets => }/translations/fi.json (100%) rename ui/{assets => }/translations/fr.json (100%) rename ui/{assets => }/translations/it.json (100%) rename ui/{assets => }/translations/nl.json (100%) rename ui/{assets => }/translations/pt_br.json (100%) rename ui/{assets => }/translations/ru.json (100%) rename ui/{assets => }/translations/sv.json (100%) rename ui/{assets => }/translations/zh.json (100%) diff --git a/.gitignore b/.gitignore index 90972df6a..2c0071a71 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ docker/lemmy_mine.hjson docker/dev/env_deploy.sh build/ .idea/ +ui/src/translations diff --git a/ui/generate_translations.js b/ui/generate_translations.js new file mode 100644 index 000000000..ee55731bf --- /dev/null +++ b/ui/generate_translations.js @@ -0,0 +1,25 @@ +fs = require('fs'); + +fs.mkdirSync('src/translations/', { recursive: true }); +fs.readdir('translations', (err, files) => { + files.forEach(filename => { + const lang = filename.split('.')[0]; + try { + const json = JSON.parse( + fs.readFileSync('translations/' + filename, 'utf8') + ); + var data = `export const ${lang} = {\n translation: {`; + for (var key in json) { + if (key in json) { + const value = json[key].replace(/"/g, '\\"'); + data = `${data}\n ${key}: "${value}",`; + } + } + data += '\n },\n};'; + const target = 'src/translations/' + lang + '.ts'; + fs.writeFileSync(target, data); + } catch (err) { + console.error(err); + } + }); +}); diff --git a/ui/package.json b/ui/package.json index f49a98fd4..d24bc2657 100644 --- a/ui/package.json +++ b/ui/package.json @@ -8,6 +8,7 @@ "scripts": { "build": "node fuse prod", "lint": "tsc --noEmit && eslint --report-unused-disable-directives --ext .js,.ts,.tsx src", + "prebuild": "node generate_translations.js", "start": "node fuse dev" }, "keywords": [], @@ -25,7 +26,6 @@ "emoji-short-name": "^1.0.0", "husky": "^4.2.1", "i18next": "^19.0.3", - "i18next-xhr-backend": "^2.0.0", "inferno": "^7.0.1", "inferno-i18next": "nimbusec-oss/inferno-i18next", "inferno-router": "^7.0.1", @@ -55,7 +55,7 @@ "ts-node": "^8.6.2", "ts-transform-classcat": "^0.0.2", "ts-transform-inferno": "^4.0.2", - "typescript": "^3.7.5" + "typescript": "^3.8.3" }, "engines": { "node": ">=8.9.0" diff --git a/ui/src/i18next.ts b/ui/src/i18next.ts index 36dc6f1d7..0ee5b1a37 100644 --- a/ui/src/i18next.ts +++ b/ui/src/i18next.ts @@ -1,24 +1,51 @@ import i18next from 'i18next'; import { getLanguage } from './utils'; -import XHR from 'i18next-xhr-backend'; +import { en } from './translations/en'; +import { eo } from './translations/eo'; +import { es } from './translations/es'; +import { de } from './translations/de'; +import { fr } from './translations/fr'; +import { sv } from './translations/sv'; +import { ru } from './translations/ru'; +import { zh } from './translations/zh'; +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'; +import { pt_br } from './translations/pt_br'; + +// https://github.com/nimbusec-oss/inferno-i18next/blob/master/tests/T.test.js#L66 +const resources = { + en, + eo, + es, + de, + zh, + fr, + sv, + ru, + nl, + it, + fi, + ca, + fa, + pt_br, +}; function format(value: any, format: any, lng: any): any { return format === 'uppercase' ? value.toUpperCase() : value; } -i18next - .use(XHR) - .init({ - debug: true, - //load: 'languageOnly', +i18next.init({ + debug: false, + // load: 'languageOnly', - // initImmediate: false, - lng: getLanguage(), - fallbackLng: 'en', - interpolation: { format }, - backend: { - loadPath: '/static/assets/translations/{{lng}}.json', - } + // initImmediate: false, + lng: getLanguage(), + fallbackLng: 'en', + resources, + interpolation: { format }, }); export { i18next as i18n, resources }; diff --git a/ui/translation_report.ts b/ui/translation_report.ts index c010aee0e..d68cb5e89 100644 --- a/ui/translation_report.ts +++ b/ui/translation_report.ts @@ -11,7 +11,7 @@ import { nl } from './src/translations/nl'; import { it } from './src/translations/it'; import { fi } from './src/translations/fi'; import { ca } from './src/translations/ca'; -import { pt_BR } from './src/translations/pt_br'; +import { pt_br } from './src/translations/pt_br'; import fs from 'fs'; const files = [ @@ -24,7 +24,7 @@ const files = [ { t: fr, n: 'fr' }, { t: it, n: 'it' }, { t: nl, n: 'nl' }, - { t: pt_BR, n: 'pt-br' }, + { t: pt_br, n: 'pt-br' }, { t: ru, n: 'ru' }, { t: sv, n: 'sv' }, { t: zh, n: 'zh' }, diff --git a/ui/assets/translations/ca.json b/ui/translations/ca.json similarity index 100% rename from ui/assets/translations/ca.json rename to ui/translations/ca.json diff --git a/ui/assets/translations/de.json b/ui/translations/de.json similarity index 100% rename from ui/assets/translations/de.json rename to ui/translations/de.json diff --git a/ui/assets/translations/en.json b/ui/translations/en.json similarity index 100% rename from ui/assets/translations/en.json rename to ui/translations/en.json diff --git a/ui/assets/translations/eo.json b/ui/translations/eo.json similarity index 100% rename from ui/assets/translations/eo.json rename to ui/translations/eo.json diff --git a/ui/assets/translations/es.json b/ui/translations/es.json similarity index 100% rename from ui/assets/translations/es.json rename to ui/translations/es.json diff --git a/ui/assets/translations/fa.json b/ui/translations/fa.json similarity index 100% rename from ui/assets/translations/fa.json rename to ui/translations/fa.json diff --git a/ui/assets/translations/fi.json b/ui/translations/fi.json similarity index 100% rename from ui/assets/translations/fi.json rename to ui/translations/fi.json diff --git a/ui/assets/translations/fr.json b/ui/translations/fr.json similarity index 100% rename from ui/assets/translations/fr.json rename to ui/translations/fr.json diff --git a/ui/assets/translations/it.json b/ui/translations/it.json similarity index 100% rename from ui/assets/translations/it.json rename to ui/translations/it.json diff --git a/ui/assets/translations/nl.json b/ui/translations/nl.json similarity index 100% rename from ui/assets/translations/nl.json rename to ui/translations/nl.json diff --git a/ui/assets/translations/pt_br.json b/ui/translations/pt_br.json similarity index 100% rename from ui/assets/translations/pt_br.json rename to ui/translations/pt_br.json diff --git a/ui/assets/translations/ru.json b/ui/translations/ru.json similarity index 100% rename from ui/assets/translations/ru.json rename to ui/translations/ru.json diff --git a/ui/assets/translations/sv.json b/ui/translations/sv.json similarity index 100% rename from ui/assets/translations/sv.json rename to ui/translations/sv.json diff --git a/ui/assets/translations/zh.json b/ui/translations/zh.json similarity index 100% rename from ui/assets/translations/zh.json rename to ui/translations/zh.json diff --git a/ui/yarn.lock b/ui/yarn.lock index 4cbc90d95..441c9d4a9 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -4572,10 +4572,10 @@ typescript@^2.6.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c" integrity sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w== -typescript@^3.7.5: - version "3.7.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae" - integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw== +typescript@^3.8.3: + version "3.8.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061" + integrity sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w== uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" From 898971cf156faa6bfdd3082f9b4e4b148745c9a3 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Sat, 29 Feb 2020 13:21:11 -0500 Subject: [PATCH 051/164] Changing to a better discussion icon. --- ui/src/components/post-listing.tsx | 2 +- ui/src/components/symbols.tsx | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index 90a8b7a11..a0e892682 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -227,7 +227,7 @@ export class PostListing extends Component { title={i18n.t('comments')} > - + ); diff --git a/ui/src/components/symbols.tsx b/ui/src/components/symbols.tsx index db1f7b5f5..b56732632 100644 --- a/ui/src/components/symbols.tsx +++ b/ui/src/components/symbols.tsx @@ -15,9 +15,9 @@ export class Symbols extends Component { xmlnsXlink="http://www.w3.org/1999/xlink" > - - bubble2 - + + message-square + image From ee7949a0fc3a6c9889778a55c236327104782783 Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Sat, 29 Feb 2020 20:25:35 +0100 Subject: [PATCH 052/164] update deploy script to pull translations from weblate --- docker/dev/deploy.sh | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/docker/dev/deploy.sh b/docker/dev/deploy.sh index 38ea61618..30500b5a0 100755 --- a/docker/dev/deploy.sh +++ b/docker/dev/deploy.sh @@ -1,24 +1,27 @@ #!/bin/sh git checkout master +# Import translations +wget "https://weblate.yerbamate.dev/download/lemmy/lemmy/?format=zip" -O /tmp/lemmy_l10n.zip +unzip -j -o /tmp/lemmy_l10n.zip -d ui/translations/ +rm /tmp/lemmy_l10n.zip + # Creating the new tag new_tag="$1" -git tag $new_tag - third_semver=$(echo $new_tag | cut -d "." -f 3) # Setting the version on the front end cd ../../ -echo "export const version: string = '$(git describe --tags)';" > "ui/src/version.ts" +echo "export const version: string = '$new_tag';" > "ui/src/version.ts" git add "ui/src/version.ts" # Setting the version on the backend -echo "pub const VERSION: &str = \"$(git describe --tags)\";" > "server/src/version.rs" +echo "pub const VERSION: &str = \"$new_tag\";" > "server/src/version.rs" git add "server/src/version.rs" # Setting the version for Ansible -git describe --tags > "ansible/VERSION" +$new_tag > "ansible/VERSION" git add "ansible/VERSION" -cd docker/dev +cd docker/dev || exit # Changing the docker-compose prod sed -i "s/dessalines\/lemmy:.*/dessalines\/lemmy:$new_tag/" ../prod/docker-compose.yml @@ -28,6 +31,7 @@ git add ../../ansible/templates/docker-compose.yml # The commit git commit -m"Version $new_tag" +git tag $new_tag # Rebuilding docker docker-compose build @@ -69,5 +73,5 @@ git push origin $new_tag git push # Pushing to any ansible deploys -cd ../../ansible +cd ../../ansible || exit ansible-playbook lemmy.yml --become From b713ee0ec5292e150b60646795bee87c6b041256 Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Sat, 29 Feb 2020 20:35:25 +0100 Subject: [PATCH 053/164] remove translation report from readme --- README.md | 30 +------------------ ui/package.json | 2 +- ui/translation_report.ts | 62 ---------------------------------------- 3 files changed, 2 insertions(+), 92 deletions(-) delete mode 100644 ui/translation_report.ts diff --git a/README.md b/README.md index ada1ad5bb..5df74893f 100644 --- a/README.md +++ b/README.md @@ -124,35 +124,7 @@ Lemmy is free, open-source software, meaning no advertising, monetizing, or vent ### Translations -If you'd like to add translations, take a look at the [English translation file](ui/assets/translations/en.json). - -- Languages supported: Brazilian Portuguese (`pt-br`), 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 | 97% | cross_posted_to,old,support_on_liberapay,couldnt_get_comments,post_title_too_long,time,action -de | 86% | cross_posted_to,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,couldnt_get_comments,post_title_too_long,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message,time,action -fa | 71% | cross_post,cross_posted_to,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,support_on_liberapay,general_sponsors,joined,by,to,from,landing_0,logged_in,couldnt_get_comments,community_moderator_already_exists,community_follower_already_exists,community_user_already_banned,post_title_too_long,no_slurs,admin_already_created,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message,time,action -eo | 73% | cross_posted_to,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,support_on_liberapay,donate_to_lemmy,donate,from,are_you_sure,yes,no,logged_in,couldnt_get_comments,post_title_too_long,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message,time,action -es | 99% | cross_posted_to,couldnt_get_comments,post_title_too_long -fi | 97% | cross_posted_to,old,support_on_liberapay,couldnt_get_comments,post_title_too_long,time,action -fr | 100% | -it | 82% | cross_posted_to,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,couldnt_get_comments,post_title_too_long,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message,time,action -nl | 98% | cross_posted_to,couldnt_get_comments,post_title_too_long,time,action -pt-br | 99% | couldnt_get_comments,post_title_too_long -ru | 70% | cross_posts,cross_post,cross_posted_to,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,support_on_liberapay,donate_to_lemmy,donate,monero,by,to,from,transfer_community,transfer_site,are_you_sure,yes,no,logged_in,couldnt_get_comments,post_title_too_long,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message,time,action -sv | 81% | cross_posted_to,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,support_on_liberapay,donate_to_lemmy,donate,from,logged_in,couldnt_get_comments,post_title_too_long,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message,time,action -zh | 69% | cross_posts,cross_post,cross_posted_to,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,couldnt_get_comments,post_title_too_long,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message,time,action - - -If you'd like to update this report, run: - -```bash -cd ui -ts-node translation_report.ts -``` +If you want to help with translating, take a look at [Weblate](https://weblate.yerbamate.dev/projects/lemmy/). ## Contact diff --git a/ui/package.json b/ui/package.json index d24bc2657..6cb2092c8 100644 --- a/ui/package.json +++ b/ui/package.json @@ -63,7 +63,7 @@ "engineStrict": true, "husky": { "hooks": { - "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" + "pre-commit": "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/translation_report.ts b/ui/translation_report.ts deleted file mode 100644 index d68cb5e89..000000000 --- a/ui/translation_report.ts +++ /dev/null @@ -1,62 +0,0 @@ -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'; -import { ru } from './src/translations/ru'; -import { nl } from './src/translations/nl'; -import { it } from './src/translations/it'; -import { fi } from './src/translations/fi'; -import { ca } from './src/translations/ca'; -import { pt_br } from './src/translations/pt_br'; -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' }, - { t: fr, n: 'fr' }, - { t: it, n: 'it' }, - { t: nl, n: 'nl' }, - { t: pt_br, n: 'pt-br' }, - { t: ru, n: 'ru' }, - { t: sv, n: 'sv' }, - { t: zh, n: 'zh' }, -]; -const masterKeys = Object.keys(en.translation); - -const readmePath = '../README.md'; - -const open = ''; -const close = ''; - -const readmeTxt = fs.readFileSync(readmePath, { encoding: 'utf8' }); - -const before = readmeTxt.split(open)[0]; -const after = readmeTxt.split(close)[1]; - -function difference(a: Array, b: Array): Array { - return a.filter(x => !b.includes(x)); -} - -const report = - 'lang | done | missing\n' + - '---- | ---- | -------\n' + - files - .map(file => { - const keys = Object.keys(file.t.translation); - const pct: number = (keys.length / masterKeys.length) * 100; - const missing = difference(masterKeys, keys); - return `${file.n} | ${pct.toFixed(0)}% | ${missing}`; - }) - .join('\n'); - -const alteredReadmeTxt = `${before}${open}\n\n${report}\n${close}${after}`; - -fs.writeFileSync(readmePath, alteredReadmeTxt); From 300828808da5ccc5d085354121336f082fa117db Mon Sep 17 00:00:00 2001 From: Dessalines Date: Sat, 29 Feb 2020 14:40:27 -0500 Subject: [PATCH 054/164] Adding generate report for yarn start. --- ui/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/package.json b/ui/package.json index 6cb2092c8..f0e9f0b72 100644 --- a/ui/package.json +++ b/ui/package.json @@ -9,6 +9,7 @@ "build": "node fuse prod", "lint": "tsc --noEmit && eslint --report-unused-disable-directives --ext .js,.ts,.tsx src", "prebuild": "node generate_translations.js", + "prestart": "node generate_translations.js", "start": "node fuse dev" }, "keywords": [], @@ -63,7 +64,7 @@ "engineStrict": true, "husky": { "hooks": { - "pre-commit": "git add ../README.md && cargo clippy --manifest-path ../server/Cargo.toml --all-targets --all-features -- -D warnings && lint-staged" + "pre-commit": "cargo clippy --manifest-path ../server/Cargo.toml --all-targets --all-features -- -D warnings && lint-staged" } }, "lint-staged": { From adba6b056f9f3bff6b55ff9863bb75ea847742e0 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Sat, 29 Feb 2020 15:18:56 -0500 Subject: [PATCH 055/164] Translation additions. - Adding Japanese. Fixes #566 - Adding some German translations. Fixes #567 - Fixing pt-br --- ui/src/i18next.ts | 6 +- ui/src/utils.ts | 4 + ui/translations/de.json | 35 +++- ui/translations/ja.json | 197 +++++++++++++++++++++ ui/translations/{pt_br.json => pt_BR.json} | 0 5 files changed, 239 insertions(+), 3 deletions(-) create mode 100644 ui/translations/ja.json rename ui/translations/{pt_br.json => pt_BR.json} (100%) diff --git a/ui/src/i18next.ts b/ui/src/i18next.ts index 0ee5b1a37..85d06f91f 100644 --- a/ui/src/i18next.ts +++ b/ui/src/i18next.ts @@ -13,7 +13,8 @@ import { it } from './translations/it'; import { fi } from './translations/fi'; import { ca } from './translations/ca'; import { fa } from './translations/fa'; -import { pt_br } from './translations/pt_br'; +import { pt_BR } from './translations/pt_BR'; +import { ja } from './translations/ja'; // https://github.com/nimbusec-oss/inferno-i18next/blob/master/tests/T.test.js#L66 const resources = { @@ -30,7 +31,8 @@ const resources = { fi, ca, fa, - pt_br, + pt_BR, + ja, }; function format(value: any, format: any, lng: any): any { diff --git a/ui/src/utils.ts b/ui/src/utils.ts index 9d2798efe..661057206 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -11,6 +11,7 @@ import 'moment/locale/fi'; import 'moment/locale/ca'; import 'moment/locale/fa'; import 'moment/locale/pt-br'; +import 'moment/locale/ja'; import { UserOperation, @@ -278,6 +279,7 @@ export const languages = [ { code: 'es', name: 'Español' }, { code: 'de', name: 'Deutsch' }, { code: 'fa', name: 'فارسی' }, + { code: 'ja', name: '日本語' }, { code: 'pt_BR', name: 'Português Brasileiro' }, { code: 'zh', name: '中文' }, { code: 'fi', name: 'Suomi' }, @@ -331,6 +333,8 @@ export function getMomentLanguage(): string { lang = 'fa'; } else if (lang.startsWith('pt')) { lang = 'pt-br'; + } else if (lang.startsWith('ja')) { + lang = 'ja'; } else { lang = 'en'; } diff --git a/ui/translations/de.json b/ui/translations/de.json index 49b4b3df6..15c437012 100644 --- a/ui/translations/de.json +++ b/ui/translations/de.json @@ -204,5 +204,38 @@ "user_already_exists": "Nutzer existiert bereits.", "couldnt_update_user": "Konnte Nutzer nicht aktualisieren", "system_err_login": - "Systemfehler. Versuche dich aus- und wieder einzuloggen." + "Systemfehler. Versuche dich aus- und wieder einzuloggen.", + "cross_posted_to": "Crossposted auf: ", + "create_private_message": "Privatnachricht erstellen", + "send_secure_message": "Sichere Nachricht absenden", + "send_message": "Nachricht absenden", + "message": "Nachricht", + "avatar": "Avatar", + "upload_avatar": "Avatar hochladen", + "show_avatars": "Avatare anzeigen", + "old": "Alt", + "docs": "Dokumentation", + "message_sent": "Nachricht versandt", + "messages": "Nachrichten", + "old_password": "Letztes Passwort", + "matrix_user_id": "Matrix Benutzer", + "private_message_disclaimer": "Achtung: Private Nachrichten sind in Lemmy nicht sicher. Bitte erstelle einen <1>Riot.im Account für sicheren Nachrichtenverkehr.", + "send_notifications_to_email": "Sende Benachrichtigungen per Email", + "downvotes_disabled": "Downvotes deaktiviert", + "enable_downvotes": "Aktiviere Downvotes", + "open_registration": "Registrierung öffnen", + "registration_closed": "Registrierung geschlossen", + "enable_nsfw": "NSFW Erlauben", + "donate_to_lemmy": "Lemmy spenden", + "donate": "Spenden", + "from": "von", + "logged_in": "Eingeloggt", + "couldnt_get_comments": "Konnte Kommentare nicht laden.", + "post_title_too_long": "Posttitel zu lang.", + "email_already_exists": "Email existiert bereits.", + "couldnt_create_private_message": "Konnte Privatnachricht nicht erstelllen.", + "no_private_message_edit_allowed": "Editieren der Privatnachricht nicht erlaubt.", + "couldnt_update_private_message": "Konnte Privatnachricht nicht aktualisieren.", + "time": "Zeit", + "action": "Aktion" } diff --git a/ui/translations/ja.json b/ui/translations/ja.json new file mode 100644 index 000000000..517db5639 --- /dev/null +++ b/ui/translations/ja.json @@ -0,0 +1,197 @@ +{ + "post": "投稿", + "remove_post": "投稿を削除", + "no_posts": "投稿はありません。", + "create_a_post": "投稿を作成", + "create_post": "投稿を作成", + "number_of_posts": "{{count}} 件の投稿", + "posts": "投稿", + "related_posts": "こちらの投稿が関連しているかもしれません", + "cross_posts": "このリンクはこちらにも投稿されています:", + "cross_post": "クロスポスト", + "cross_posted_to": "こちらにも投稿済み: ", + "comments": "コメント", + "number_of_comments": "{{count}} 件のコメント", + "remove_comment": "コミュニティを削除", + "communities": "コミュニティ", + "users": "ユーザー", + "create_a_community": "コミュニティを作成", + "create_community": "コミュニティを作成", + "remove_community": "コミュニティを削除", + "subscribed_to_communities": "登録済みの<1>コミュニティ", + "trending_communities": "話題の<1>コミュニティ", + "list_of_communities": "コミュニティ一覧", + "number_of_communities": "{{count}} 個のコミュニティ", + "community_reqs": "英小文字、アンダースコア、空白なし。", + "create_private_message": "プライベートメッセージの作成", + "send_secure_message": "メッセージを安全に送信", + "send_message": "メッセージを送信", + "message": "メッセージ", + "edit": "編集", + "reply": "返信", + "cancel": "キャンセル", + "preview": "プレビュー", + "upload_image": "画像をアップロード", + "avatar": "アバター", + "upload_avatar": "アバターをアップロード", + "show_avatars": "アバターを表示", + "formatting_help": "書式のヘルプ", + "view_source": "ソースを表示", + "unlock": "凍結解除", + "lock": "凍結", + "sticky": "固定", + "unsticky": "固定解除", + "link": "リンク", + "archive_link": "リンクをアーカイブ", + "mod": "モデレーター", + "mods": "モデレーター", + "moderates": "モデレーター", + "settings": "設定", + "remove_as_mod": "モデレーターから除外", + "appoint_as_mod": "モデレーターに任命", + "modlog": "モデレーションログ", + "admin": "管理者", + "admins": "管理者", + "remove_as_admin": "管理者から除外", + "appoint_as_admin": "管理者に任命", + "remove": "強制削除", + "removed": "強制削除済み", + "locked": "凍結中", + "stickied": "固定中", + "reason": "理由", + "mark_as_read": "既読にする", + "mark_as_unread": "未読にする", + "delete": "削除", + "deleted": "削除済み", + "delete_account": "アカウントを削除", + "delete_account_confirm": "警告: あなたのデータを全て恒久的に削除します。確認のためパスワードを入力してください。", + "restore": "復元", + "ban": "アクセス禁止", + "ban_from_site": "サイトへアクセス禁止", + "unban": "アクセス禁止解除", + "unban_from_site": "サイトへのアクセス禁止解除", + "banned": "アクセス禁止中", + "save": "保存", + "unsave": "保存解除", + "create": "作成", + "creator": "投稿者", + "username": "ユーザー名", + "email_or_username": "メールアドレスまたはユーザー名", + "number_of_users": "{{count}} 名のユーザー", + "number_of_subscribers": "{{count}} 名の登録者", + "number_of_points": "{{count}} ポイント", + "number_online": "{{count}} 名のユーザーがオンライン", + "name": "名前", + "title": "タイトル", + "category": "カテゴリー", + "subscribers": "登録者", + "saved": "保存済み", + "unsubscribe": "登録解除", + "subscribe": "登録", + "subscribed": "登録済み", + "prev": "前", + "next": "次", + "sidebar": "説明", + "sort_type": "並び順の種類", + "hot": "人気", + "new": "新しい順", + "old": "古い順", + "top_day": "日間トップ", + "week": "週間", + "month": "月間", + "year": "年間", + "all": "全て", + "top": "トップ", + "api": "API", + "docs": "ドキュメント", + "inbox": "受信箱", + "inbox_for": "<1>{{user}} の受信箱", + "mark_all_as_read": "全てを既読にする", + "type": "種類", + "unread": "未読", + "replies": "返信", + "mentions": "言及", + "reply_sent": "返信を送信しました", + "message_sent": "メッセージを送信しました", + "search": "検索", + "overview": "全種類", + "view": "表示", + "logout": "ログアウト", + "login_sign_up": "ログイン / 登録", + "login": "ログイン", + "sign_up": "登録", + "notifications_error": "お遣いのブラウザーではデスクトップ通知が利用不可能です。Firefox か Chrome をお試しください。", + "unread_messages": "未読のメッセージ", + "messages": "メッセージ", + "password": "パスワード", + "verify_password": "パスワードの確認", + "old_password": "現在のパスワード", + "forgot_password": "パスワードを忘れた", + "reset_password_mail_sent": "パスワードリセット用のメールを送信しました。", + "password_change": "パスワードの変更", + "new_password": "新しいパスワード", + "no_email_setup": "サーバーのメール設定が正しくありません。", + "email": "メールアドレス", + "matrix_user_id": "Matrix のユーザーアカウント", + "private_message_disclaimer": "Lemmy でのプライベートメッセージは安全ではありません。安全なメッセージを送るには <1>Riot.im でアカウントを作成してください。", + "send_notifications_to_email": "通知をメール送信", + "optional": "任意", + "language": "言語", + "browser_default": "ブラウザーのデフォルト", + "enable_downvotes": "反対票を有効化", + "open_registration": "ユーザー登録を受け付ける", + "registration_closed": "登録は受け付けていません", + "enable_nsfw": "閲覧注意を有効化", + "url": "URL", + "body": "本文", + "copy_suggested_title": "タイトルの提案をコピーする: {{title}}", + "community": "コミュニティ", + "expand_here": "拡大表示", + "subscribe_to_communities": "<1>コミュニティをいくつか登録してみましょう。", + "recent_comments": "最新コメント", + "no_results": "検索結果なし。", + "setup": "設定", + "lemmy_instance_setup": "Lemmy インスタンス設定", + "setup_admin": "サイト管理者を設定", + "your_site": ":サイト", + "modified": "変更", + "nsfw": "閲覧注意", + "show_nsfw": "閲覧注意のコンテンツを表示", + "theme": "テーマ", + "sponsors": "スポンサー", + "sponsor_message": "Lemmy は自由で<1>オープンソースの、つまり広告も収益化もベンチャーキャピタルも決してないソフトウェアです。あなたの寄付はプロジェクトの開発を直接支援します。次の方々に感謝します:", + "support_on_patreon": "Patreon で支援", + "support_on_liberapay": "Liberapay で支援", + "donate_to_lemmy": "Lemmy に寄付", + "donate": "寄付", + "general_sponsors": "ジェネラルスポンサーは Lemmy に $10 ~ $39 の支払いを申し出てくれた方達です。", + "crypto": "暗号通貨", + "bitcoin": "ビットコイン", + "ethereum": "Ethereum", + "monero": "Monero", + "code": "コード", + "joined": "登録", + "by": "投稿者", + "to": "宛先", + "transfer_community": "コミュニティを譲渡", + "transfer_site": "サイトを譲渡", + "are_you_sure": "本当によろしいですか?", + "yes": "はい", + "no": "いいえ", + "powered_by": "Powered by", + "landing_0": "Lemmy は<1>リンクアグリゲーター / Reddit 代替ソフトウェアで、<2>fediverse 上で動作することを目的にしています。<3>セルフホスト可能で、リアルタイム更新のコメントスレッドがあり、小さい(<4>80kB以下)ものです。ActivityPub ネットワークへの連合が計画にあります。<5>まだ<6>初期のベータバージョンで、現在は多くの機能がうまく動作しないか実装されていません。<7>新機能の提案やバグの報告は<8>こちらでお願いします。<9><10>Rust、<11>Actix、<12>Inferno、<13>Typescriptで作っています。", + "not_logged_in": "ログインしていません。", + "logged_in": "ログインしました。", + "community_ban": "このコミュニティへのアクセスを禁止されています。", + "site_ban": "サイトへのアクセスを禁止されています。", + "couldnt_create_comment": "投稿を作成できませんでした。", + "couldnt_find_community": "コミュニティが見付かりません。", + "couldnt_find_post": "投稿が見付かりません。", + "couldnt_find_that_username_or_email": "ユーザー名またはメールアドレスが見付かりません。", + "password_incorrect": "パスワードが不正です。", + "passwords_dont_match": "パスワードが一致しません。", + "admin_already_created": "済みませんが、既に管理者が設定されています。", + "email_already_exists": "メールアドレスが既に使用されています", + "time": "時刻", + "action": "行動" +} diff --git a/ui/translations/pt_br.json b/ui/translations/pt_BR.json similarity index 100% rename from ui/translations/pt_br.json rename to ui/translations/pt_BR.json From f607e98565c2fb042a36640cb6015a66f455889e Mon Sep 17 00:00:00 2001 From: Dessalines Date: Sat, 29 Feb 2020 15:28:30 -0500 Subject: [PATCH 056/164] Version v0.6.27 --- ansible/VERSION | 2 +- docker/prod/docker-compose.yml | 2 +- server/src/version.rs | 2 +- ui/src/version.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ansible/VERSION b/ansible/VERSION index f37037aee..898f3498c 100644 --- a/ansible/VERSION +++ b/ansible/VERSION @@ -1 +1 @@ -v0.6.26 +v0.6.27 diff --git a/docker/prod/docker-compose.yml b/docker/prod/docker-compose.yml index 13fa5508c..0cdcbe060 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.26 + image: dessalines/lemmy:v0.6.27 ports: - "127.0.0.1:8536:8536" restart: always diff --git a/server/src/version.rs b/server/src/version.rs index 81456d0b2..a7c04a1f6 100644 --- a/server/src/version.rs +++ b/server/src/version.rs @@ -1 +1 @@ -pub const VERSION: &str = "v0.6.26"; +pub const VERSION: &str = "v0.6.27"; diff --git a/ui/src/version.ts b/ui/src/version.ts index 8eb5e2cd8..f798adf28 100644 --- a/ui/src/version.ts +++ b/ui/src/version.ts @@ -1 +1 @@ -export const version: string = 'v0.6.26'; +export const version: string = 'v0.6.27'; From 565524a80ba107e4985a2ac6b7bf790572468ce8 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Sat, 29 Feb 2020 15:49:09 -0500 Subject: [PATCH 057/164] Adding git add to deploy. --- docker/dev/deploy.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docker/dev/deploy.sh b/docker/dev/deploy.sh index 30500b5a0..94a2fa91e 100755 --- a/docker/dev/deploy.sh +++ b/docker/dev/deploy.sh @@ -3,7 +3,8 @@ git checkout master # Import translations wget "https://weblate.yerbamate.dev/download/lemmy/lemmy/?format=zip" -O /tmp/lemmy_l10n.zip -unzip -j -o /tmp/lemmy_l10n.zip -d ui/translations/ +unzip -j -o /tmp/lemmy_l10n.zip -d ../../ui/translations/ +git add ../../ui/translations rm /tmp/lemmy_l10n.zip # Creating the new tag @@ -18,7 +19,7 @@ git add "ui/src/version.ts" echo "pub const VERSION: &str = \"$new_tag\";" > "server/src/version.rs" git add "server/src/version.rs" # Setting the version for Ansible -$new_tag > "ansible/VERSION" +echo $new_tag > "ansible/VERSION" git add "ansible/VERSION" cd docker/dev || exit From 7680971b896f67dd6bc02b464bfd8a940ccb5d87 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Sat, 29 Feb 2020 17:16:42 -0500 Subject: [PATCH 058/164] Increasing mini-overlay size. #564 --- ui/assets/css/main.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/assets/css/main.css b/ui/assets/css/main.css index fd51c5948..b03f27036 100644 --- a/ui/assets/css/main.css +++ b/ui/assets/css/main.css @@ -198,6 +198,8 @@ hr { top: 0; right: 0; padding: 2px; + height: 1.5em; + width: 1.5em; background: rgba(0,0,0,.4); border-bottom-left-radius: 0.25rem !important; border-top-right-radius: 0.25rem !important; From 93cb0892ae3450c5861fbcc38b06507cc1beb534 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Sat, 29 Feb 2020 21:04:42 -0500 Subject: [PATCH 059/164] Some fixes for mobile view. --- ui/src/components/post-listing.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index a0e892682..1fdfad2d5 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -249,7 +249,9 @@ export class PostListing extends Component { -
{this.state.score}
+
+ {this.state.score} +
{WebSocketService.Instance.site.enable_downvotes && (
{!this.state.imageExpanded && ( -
+
{this.thumbnail()}
)} @@ -274,14 +276,14 @@ export class PostListing extends Component { muted loop controls - class="col-2 pr-0 mt-1" + class="col-11 col-sm-2 pr-0 mt-1" height="100" width="150" > )} -
+
From 399be2370d375e49183968ba56872c0c198e1812 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Sat, 29 Feb 2020 22:06:42 -0500 Subject: [PATCH 060/164] Adding more for advanced actions on comments and posts. - Fixes #561 --- ui/src/components/comment-node.tsx | 469 +++++++++++++++-------------- ui/src/components/post-listing.tsx | 368 ++++++++++++---------- ui/translations/en.json | 1 + 3 files changed, 452 insertions(+), 386 deletions(-) diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index 1d0b12cad..a904eae36 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -48,6 +48,7 @@ interface CommentNodeState { showConfirmAppointAsAdmin: boolean; collapsed: boolean; viewSource: boolean; + showAdvanced: boolean; my_vote: number; score: number; upvotes: number; @@ -81,6 +82,7 @@ export class CommentNode extends Component { banType: BanType.Community, collapsed: false, viewSource: false, + showAdvanced: false, showConfirmTransferSite: false, showConfirmTransferCommunity: false, showConfirmAppointAsMod: false, @@ -300,93 +302,259 @@ export class CommentNode extends Component { )} -
  • -
  • - - {i18n.t('view_source')} - -
  • -
  • - - {i18n.t('link')} - -
  • - {/* Admins and mods can remove comments */} - {(this.canMod || this.canAdmin) && ( + {!this.state.showAdvanced ? ( +
  • + + {i18n.t('more')} + +
  • + ) : ( <>
  • - {!node.comment.removed ? ( - - {i18n.t('remove')} - - ) : ( - - {i18n.t('restore')} - - )} + + {i18n.t('view_source')} +
  • - - )} - {/* Mods can ban from community, and appoint as mods to community */} - {this.canMod && ( - <> - {!this.isMod && ( -
  • - {!node.comment.banned_from_community ? ( - - {i18n.t('ban')} - - ) : ( - - {i18n.t('unban')} - - )} -
  • +
  • + + {i18n.t('link')} + +
  • + {/* Admins and mods can remove comments */} + {(this.canMod || this.canAdmin) && ( + <> +
  • +
  • + {!node.comment.removed ? ( + + {i18n.t('remove')} + + ) : ( + + {i18n.t('restore')} + + )} +
  • + )} - {!node.comment.banned_from_community && ( + {/* Mods can ban from community, and appoint as mods to community */} + {this.canMod && ( + <> + {!this.isMod && ( +
  • + {!node.comment.banned_from_community ? ( + + {i18n.t('ban')} + + ) : ( + + {i18n.t('unban')} + + )} +
  • + )} + {!node.comment.banned_from_community && ( +
  • + {!this.state.showConfirmAppointAsMod ? ( + + {this.isMod + ? i18n.t('remove_as_mod') + : i18n.t('appoint_as_mod')} + + ) : ( + <> + + {i18n.t('are_you_sure')} + + + {i18n.t('yes')} + + + {i18n.t('no')} + + + )} +
  • + )} + + )} + {/* Community creators and admins can transfer community to another mod */} + {(this.amCommunityCreator || this.canAdmin) && + this.isMod && ( +
  • + {!this.state.showConfirmTransferCommunity ? ( + + {i18n.t('transfer_community')} + + ) : ( + <> + + {i18n.t('are_you_sure')} + + + {i18n.t('yes')} + + + {i18n.t('no')} + + + )} +
  • + )} + {/* Admins can ban from all, and appoint other admins */} + {this.canAdmin && ( + <> + {!this.isAdmin && ( +
  • + {!node.comment.banned ? ( + + {i18n.t('ban_from_site')} + + ) : ( + + {i18n.t('unban_from_site')} + + )} +
  • + )} + {!node.comment.banned && ( +
  • + {!this.state.showConfirmAppointAsAdmin ? ( + + {this.isAdmin + ? i18n.t('remove_as_admin') + : i18n.t('appoint_as_admin')} + + ) : ( + <> + + {i18n.t('are_you_sure')} + + + {i18n.t('yes')} + + + {i18n.t('no')} + + + )} +
  • + )} + + )} + {/* Site Creator can transfer to another admin */} + {this.amSiteCreator && this.isAdmin && (
  • - {!this.state.showConfirmAppointAsMod ? ( + {!this.state.showConfirmTransferSite ? ( - {this.isMod - ? i18n.t('remove_as_mod') - : i18n.t('appoint_as_mod')} + {i18n.t('transfer_site')} ) : ( <> @@ -397,7 +565,7 @@ export class CommentNode extends Component { class="pointer d-inline-block mr-1" onClick={linkEvent( this, - this.handleAddModToCommunity + this.handleTransferSite )} > {i18n.t('yes')} @@ -406,7 +574,7 @@ export class CommentNode extends Component { class="pointer d-inline-block" onClick={linkEvent( this, - this.handleCancelConfirmAppointAsMod + this.handleCancelShowConfirmTransferSite )} > {i18n.t('no')} @@ -417,148 +585,6 @@ export class CommentNode extends Component { )} )} - {/* Community creators and admins can transfer community to another mod */} - {(this.amCommunityCreator || this.canAdmin) && this.isMod && ( -
  • - {!this.state.showConfirmTransferCommunity ? ( - - {i18n.t('transfer_community')} - - ) : ( - <> - - {i18n.t('are_you_sure')} - - - {i18n.t('yes')} - - - {i18n.t('no')} - - - )} -
  • - )} - {/* Admins can ban from all, and appoint other admins */} - {this.canAdmin && ( - <> - {!this.isAdmin && ( -
  • - {!node.comment.banned ? ( - - {i18n.t('ban_from_site')} - - ) : ( - - {i18n.t('unban_from_site')} - - )} -
  • - )} - {!node.comment.banned && ( -
  • - {!this.state.showConfirmAppointAsAdmin ? ( - - {this.isAdmin - ? i18n.t('remove_as_admin') - : i18n.t('appoint_as_admin')} - - ) : ( - <> - - {i18n.t('are_you_sure')} - - - {i18n.t('yes')} - - - {i18n.t('no')} - - - )} -
  • - )} - - )} - {/* Site Creator can transfer to another admin */} - {this.amSiteCreator && this.isAdmin && ( -
  • - {!this.state.showConfirmTransferSite ? ( - - {i18n.t('transfer_site')} - - ) : ( - <> - - {i18n.t('are_you_sure')} - - - {i18n.t('yes')} - - - {i18n.t('no')} - - - )} -
  • - )} )} @@ -1016,4 +1042,9 @@ export class CommentNode extends Component { i.state.viewSource = !i.state.viewSource; i.setState(i.state); } + + handleShowAdvanced(i: CommentNode) { + i.state.showAdvanced = !i.state.showAdvanced; + i.setState(i.state); + } } diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index 1fdfad2d5..d57aa6636 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -45,6 +45,7 @@ interface PostListingState { showConfirmTransferCommunity: boolean; imageExpanded: boolean; viewSource: boolean; + showAdvanced: boolean; my_vote: number; score: number; upvotes: number; @@ -75,6 +76,7 @@ export class PostListing extends Component { showConfirmTransferCommunity: false, imageExpanded: false, viewSource: false, + showAdvanced: false, my_vote: this.props.post.my_vote, score: this.props.post.score, upvotes: this.props.post.upvotes, @@ -511,211 +513,238 @@ export class PostListing extends Component { )} - {this.canModOnSelf && ( - <> -
  • - - {post.locked ? i18n.t('unlock') : i18n.t('lock')} - -
  • -
  • - - {post.stickied - ? i18n.t('unsticky') - : i18n.t('sticky')} - -
  • - - )} - {/* Mods can ban from community, and appoint as mods to community */} - {(this.canMod || this.canAdmin) && ( + + {!this.state.showAdvanced && this.props.showBody ? (
  • - {!post.removed ? ( - - {i18n.t('remove')} - - ) : ( - - {i18n.t('restore')} - - )} + + {i18n.t('more')} +
  • - )} - {this.canMod && ( + ) : ( <> - {!this.isMod && ( + {this.canModOnSelf && ( + <> +
  • + + {post.locked + ? i18n.t('unlock') + : i18n.t('lock')} + +
  • +
  • + + {post.stickied + ? i18n.t('unsticky') + : i18n.t('sticky')} + +
  • + + )} + {/* Mods can ban from community, and appoint as mods to community */} + {(this.canMod || this.canAdmin) && (
  • - {!post.banned_from_community ? ( + {!post.removed ? ( - {i18n.t('ban')} + {i18n.t('remove')} ) : ( - {i18n.t('unban')} + {i18n.t('restore')} )}
  • )} - {!post.banned_from_community && ( -
  • - - {this.isMod - ? i18n.t('remove_as_mod') - : i18n.t('appoint_as_mod')} - -
  • - )} - - )} - {/* Community creators and admins can transfer community to another mod */} - {(this.amCommunityCreator || this.canAdmin) && this.isMod && ( -
  • - {!this.state.showConfirmTransferCommunity ? ( - - {i18n.t('transfer_community')} - - ) : ( + {this.canMod && ( <> - - {i18n.t('are_you_sure')} - - - {i18n.t('yes')} - - - {i18n.t('no')} - + {!this.isMod && ( +
  • + {!post.banned_from_community ? ( + + {i18n.t('ban')} + + ) : ( + + {i18n.t('unban')} + + )} +
  • + )} + {!post.banned_from_community && ( +
  • + + {this.isMod + ? i18n.t('remove_as_mod') + : i18n.t('appoint_as_mod')} + +
  • + )} )} - - )} - {/* Admins can ban from all, and appoint other admins */} - {this.canAdmin && ( - <> - {!this.isAdmin && ( + {/* Community creators and admins can transfer community to another mod */} + {(this.amCommunityCreator || this.canAdmin) && + this.isMod && ( +
  • + {!this.state.showConfirmTransferCommunity ? ( + + {i18n.t('transfer_community')} + + ) : ( + <> + + {i18n.t('are_you_sure')} + + + {i18n.t('yes')} + + + {i18n.t('no')} + + + )} +
  • + )} + {/* Admins can ban from all, and appoint other admins */} + {this.canAdmin && ( + <> + {!this.isAdmin && ( +
  • + {!post.banned ? ( + + {i18n.t('ban_from_site')} + + ) : ( + + {i18n.t('unban_from_site')} + + )} +
  • + )} + {!post.banned && ( +
  • + + {this.isAdmin + ? i18n.t('remove_as_admin') + : i18n.t('appoint_as_admin')} + +
  • + )} + + )} + {/* Site Creator can transfer to another admin */} + {this.amSiteCreator && this.isAdmin && (
  • - {!post.banned ? ( - - {i18n.t('ban_from_site')} - - ) : ( + {!this.state.showConfirmTransferSite ? ( - {i18n.t('unban_from_site')} + {i18n.t('transfer_site')} + ) : ( + <> + + {i18n.t('are_you_sure')} + + + {i18n.t('yes')} + + + {i18n.t('no')} + + )}
  • )} - {!post.banned && ( -
  • - - {this.isAdmin - ? i18n.t('remove_as_admin') - : i18n.t('appoint_as_admin')} - -
  • - )} )} - {/* Site Creator can transfer to another admin */} - {this.amSiteCreator && this.isAdmin && ( -
  • - {!this.state.showConfirmTransferSite ? ( - - {i18n.t('transfer_site')} - - ) : ( - <> - - {i18n.t('are_you_sure')} - - - {i18n.t('yes')} - - - {i18n.t('no')} - - - )} -
  • - )} )} {this.props.showBody && post.body && ( @@ -1201,4 +1230,9 @@ export class PostListing extends Component { i.state.viewSource = !i.state.viewSource; i.setState(i.state); } + + handleShowAdvanced(i: PostListing) { + i.state.showAdvanced = !i.state.showAdvanced; + i.setState(i.state); + } } diff --git a/ui/translations/en.json b/ui/translations/en.json index 54460022e..8b719c712 100644 --- a/ui/translations/en.json +++ b/ui/translations/en.json @@ -29,6 +29,7 @@ "message": "Message", "edit": "edit", "reply": "reply", + "more": "more", "cancel": "Cancel", "preview": "Preview", "upload_image": "upload image", From 59391b23694ed6ade2db9b8b5c7d2aea7b25a1bb Mon Sep 17 00:00:00 2001 From: Dessalines Date: Sat, 29 Feb 2020 22:16:52 -0500 Subject: [PATCH 061/164] Moving view source. --- ui/src/components/post-listing.tsx | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index d57aa6636..519c7f740 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -525,6 +525,16 @@ export class PostListing extends Component { ) : ( <> + {this.props.showBody && post.body && ( +
  • + + {i18n.t('view_source')} + +
  • + )} {this.canModOnSelf && ( <>
  • @@ -747,16 +757,6 @@ export class PostListing extends Component { )} )} - {this.props.showBody && post.body && ( -
  • - - {i18n.t('view_source')} - -
  • - )} {this.state.showRemoveDialog && (
    Date: Sat, 29 Feb 2020 20:38:04 +0000 Subject: [PATCH 062/164] Translated using Weblate (French) Currently translated at 100.0% (229 of 229 strings) Translation: Lemmy/lemmy Translate-URL: http://127.0.0.1/projects/lemmy/lemmy/fr/ --- ui/translations/fr.json | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/ui/translations/fr.json b/ui/translations/fr.json index 5f07d73ae..cd37b9e39 100644 --- a/ui/translations/fr.json +++ b/ui/translations/fr.json @@ -9,7 +9,7 @@ "related_posts": "Ces sujets peuvent être corrélés", "cross_posts": "Ce sujet a également été posté sur :", "cross_post": "publication croisée", - "cross_posted_to": "publication croisée à", + "cross_posted_to": "publication croisée à : ", "comments": "Commentaires", "number_of_comments": "{{count}} Commentaires", "remove_comment": "Supprimer le commentaire", @@ -64,8 +64,7 @@ "delete": "supprimer", "deleted": "supprimé", "delete_account": "Supprimer le compte", - "delete_account_confirm": - "Attention: cette action supprime toutes vos données de façons permanente ! Entrez votre mot de passe pour confirmer.", + "delete_account_confirm": "Avertissement : cette action supprimera toutes vos données de façons permanente ! Saisissez votre mot de passe pour confirmer.", "restore": "restaurer", "ban": "bannir", "ban_from_site": "bannir du site", @@ -122,8 +121,7 @@ "login_sign_up": "Se connecter / S'inscrire", "login": "Se connecter", "sign_up": "S'inscrire", - "notifications_error": - "Les notifications de bureau ne sont pas discponibles sur votre navigateur. Essayez Firefox ou Chrome.", + "notifications_error": "Les notifications de bureau ne sont pas discponibles sur votre navigateur. Essayez Firefox ou Chrome.", "unread_messages": "Messages non-lu", "messages": "Messages", "password": "Mot de passe", @@ -136,8 +134,7 @@ "no_email_setup": "Ce serveur n'a pas correctement configuré la messagerie de email.", "email": "Email", "matrix_user_id": "Utilisateur Matrix", - "private_message_disclaimer": - "Attention: les messages privés en Matrix ne sont pas sécurisés. S'il vous plait, créer un compte de <1>Riot.im pour des messages sécurisés.", + "private_message_disclaimer": "Attention: les messages privés dans Lemmy ne sont pas sécurisés. Veuillez créer un compte sur <1>Riot.im pour pouvoir utiliser envoyer des messages sécurisés.", "send_notifications_to_email": "Envoyer des notifications par email", "optional": "Optionnel", "expires": "Expire", @@ -150,7 +147,7 @@ "enable_nsfw": "Activer NSFW", "url": "URL", "body": "Texte", - "copy_suggested_title": "Ajouter le titre suggéré: {{title}}", + "copy_suggested_title": "copier le titre suggéré : {{title}}", "community": "Communauté", "expand_here": "Développer ici", "subscribe_to_communities": "S'abonner à quelques <1>communautés.", @@ -167,8 +164,7 @@ "theme": "Thème", "sponsors": "Sponsors", "sponsors_of_lemmy": "Sponsors de Lemmy", - "sponsor_message": - "Lemmy est gratuit et <1>open-source, c'est à dire sans publicité et sans monétisation. Pour toujours. Vos dons soutiennent directement le développement du projet. Merci à nos soutiens.", + "sponsor_message": "Lemmy est un logiciel libre et <1>open-source, c’est à dire, il fonctionne sans publicité et sans monétisation aucune. Vos dons soutiennent directement le développement du projet à temps plein. Merci à toutes ces personnes :", "support_on_patreon": "Soutenir sur Patreon", "support_on_liberapay": "Soutenir sur Liberapay", "donate_to_lemmy": "Faire un don à Lemmy", @@ -189,8 +185,7 @@ "yes": "oui", "no": "non", "powered_by": "Propulsé par", - "landing_0": - "Lemmy est un <1>aggrégateur de lien, similaire à reddit et conçu pour fonctionner sur le <2>fédiverse.<3>Il est auto-hébergeable, se met à jour en direct et est léger (<4>~80kB). La fédération via Activitypub est prévue. <5>Lemmy est une <6>version beta très précoce, et de nombreuses fonctionnalités sont manquantes ou non fonctionnelles. <7>Vous pouvez rapporter des bugs et suggérez de nouvelles fonctionnalités <8>ici.<9>Crée avec <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.", + "landing_0": "Lemmy est un <1>aggrégateur de lien, similaire à reddit et conçu pour fonctionner sur le <2>fédiverse.<3>Il est auto-hébergeable, se met à jour en direct et est léger (<4>~80kB). La fédération via Activitypub est prévue. <5>Lemmy est une <6>version beta très précoce, et de nombreuses fonctionnalités sont manquantes ou non fonctionnelles. <7>Vous pouvez rapporter des bugs et suggérez de nouvelles fonctionnalités <8>ici.<9>Crée avec <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.", "not_logged_in": "Vous n'êtes pas connecté.", "logged_in": "Vous êtes connecté.", "community_ban": "Vous avez été banni de cette communauté.", @@ -200,11 +195,9 @@ "couldnt_update_comment": "Impossible de mettre à jour le commentaire.", "couldnt_save_comment": "Impossible de sauvegarder le commentaire.", "couldnt_get_comments": "Impossible de obtenir les commentaires.", - "no_comment_edit_allowed": - "Vous n'êtes pas autorisé à éditer ce commentaire.", + "no_comment_edit_allowed": "Vous n'êtes pas autorisé à éditer ce commentaire.", "no_post_edit_allowed": "Vous n'êtes pas autorisé à éditer sujet.", - "no_community_edit_allowed": - "Vous n'êtes pas autorisé à éditer cette communauté.", + "no_community_edit_allowed": "Vous n'êtes pas autorisé à éditer cette communauté.", "couldnt_find_community": "Impossible de trouver cette communauté.", "couldnt_update_community": "Impossible d'éditer cette communauté.", "community_already_exists": "Cette communauté existe déjà.", @@ -222,16 +215,14 @@ "not_an_admin": "Pas administrateur.", "site_already_exists": "Le site existe déjà.", "couldnt_update_site": "Impossible de mettre à jour le site.", - "couldnt_find_that_username_or_email": - "Impossible de trouver cet utilisateur ou cet email.", + "couldnt_find_that_username_or_email": "Impossible de trouver cet utilisateur ou cet email.", "password_incorrect": "Mot de passe incorrect.", "passwords_dont_match": "Les mots de passes ne correspondent pas..", "admin_already_created": "Désolé, il y a déjà un admin.", "user_already_exists": "L'utilisateur existe déjà.", - "email_already_exists": "L'email existe déjà", + "email_already_exists": "L’email existe déjà.", "couldnt_update_user": "Impossible de mettre à jour l'utilisateur.", - "system_err_login": - "Erreur système. Essayez de vous déconneter puis de vous reconnecter.", + "system_err_login": "Erreur système. Essayez de vous déconneter puis de vous reconnecter.", "couldnt_create_private_message": "Impossible de créer un message privé.", "no_private_message_edit_allowed": "Pas autorisé à modifier un message privé.", "couldnt_update_private_message": "Impossible de modifier un message privé.", From ee1079817815894b72792dd8768a35b984d4136f Mon Sep 17 00:00:00 2001 From: ButterflyOfFire Date: Sat, 29 Feb 2020 22:05:30 +0000 Subject: [PATCH 063/164] Translated using Weblate (French) Currently translated at 100.0% (229 of 229 strings) Translation: Lemmy/lemmy Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/fr/ --- ui/translations/fr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/translations/fr.json b/ui/translations/fr.json index cd37b9e39..7f61cbfee 100644 --- a/ui/translations/fr.json +++ b/ui/translations/fr.json @@ -134,7 +134,7 @@ "no_email_setup": "Ce serveur n'a pas correctement configuré la messagerie de email.", "email": "Email", "matrix_user_id": "Utilisateur Matrix", - "private_message_disclaimer": "Attention: les messages privés dans Lemmy ne sont pas sécurisés. Veuillez créer un compte sur <1>Riot.im pour pouvoir utiliser envoyer des messages sécurisés.", + "private_message_disclaimer": "Attention : les messages privés dans Lemmy ne sont pas sécurisés. Veuillez créer un compte sur <1>Riot.im pour pouvoir envoyer des messages sécurisés.", "send_notifications_to_email": "Envoyer des notifications par email", "optional": "Optionnel", "expires": "Expire", From c15ac96053e3bc1f65412bf3fd868a048cbb82ee Mon Sep 17 00:00:00 2001 From: ButterflyOfFire Date: Sat, 29 Feb 2020 22:07:49 +0000 Subject: [PATCH 064/164] Added translation using Weblate (Arabic) --- ui/translations/ar.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 ui/translations/ar.json diff --git a/ui/translations/ar.json b/ui/translations/ar.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/ui/translations/ar.json @@ -0,0 +1 @@ +{} From e638aeb778e29de6eab01dcb1d99032954722293 Mon Sep 17 00:00:00 2001 From: ButterflyOfFire Date: Sat, 29 Feb 2020 22:09:33 +0000 Subject: [PATCH 065/164] Translated using Weblate (Arabic) Currently translated at 55.0% (126 of 229 strings) Translation: Lemmy/lemmy Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/ar/ --- ui/translations/ar.json | 129 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 128 insertions(+), 1 deletion(-) diff --git a/ui/translations/ar.json b/ui/translations/ar.json index 0967ef424..d47561395 100644 --- a/ui/translations/ar.json +++ b/ui/translations/ar.json @@ -1 +1,128 @@ -{} +{ + "creator": "المؤلّف", + "username": "إسم المستخدم", + "post": "منشور", + "remove_post": "احذف المنشور", + "no_posts": "ليس هناك منشورات.", + "create_a_post": "أنشئ منشورا", + "create_post": "إنشاء منشور", + "posts": "منشورات", + "comments": "التعليقات", + "number_of_posts": "{{count}} منشورات", + "related_posts": "يمكن لهذه المنشورات أن تكون ذات صلة", + "communities": "المجتمعات", + "users": "المستخدِمون", + "create_community": "إنشاء مجتمع", + "remove_community": "حذف المجتمع", + "list_of_communities": "قائمة المجتمعات", + "send_secure_message": "ارسل الرسالة المؤمنة", + "send_message": "أرسل الرسالة", + "message": "رسالة", + "edit": "تعديل", + "reply": "رد", + "cancel": "إلغاء", + "preview": "معاينة", + "upload_image": "إرسال صورة", + "avatar": "الصورة الرمزية", + "show_avatars": "إظهار الصور الرمزية", + "view_source": "عرض المصدر", + "sticky": "تدبيس", + "unsticky": "فك التدبيس", + "link": "الرابط", + "mod": "مشرِف", + "moderates": "إشراف", + "settings": "الإعدادات", + "remove_as_mod": "إزالة كمُشرِف", + "appoint_as_mod": "تعيين كمُشرِف", + "admin": "مدير", + "admins": "المدراء", + "remove_as_admin": "إزالة كمدير", + "appoint_as_admin": "تعيين كمدير", + "remove": "إزالة", + "removed": "تمت إزالته", + "reason": "السبب", + "mark_as_read": "تعيين كمقروء", + "mark_as_unread": "تعيين كغير مقروء بعد", + "delete": "حذف", + "deleted": "تم حذفه", + "restore": "استعادة", + "ban": "طرد", + "ban_from_site": "طرده مِن الموقع", + "banned": "مطرود", + "save": "حفظ", + "number_of_users": "{{count}} مستخدِمين", + "number_of_points": "{{count}} نقاط", + "number_online": "{{count}} مستخدمين متّصلين", + "name": "الإسم", + "title": "العنوان", + "category": "الفئة", + "subscribers": "المتابِعون", + "both": "كلاهما", + "saved": "تم حفظه", + "subscribe": "اتبع", + "prev": "السابقة", + "next": "التالية", + "sidebar": "الشريط الجانبي", + "sort_type": "ترتيب حسب", + "hot": "المتداولة", + "new": "جديد", + "old": "قديم", + "month": "شهر", + "year": "سنة", + "all": "الكل", + "docs": "الدليل", + "type": "النوع", + "replies": "الإجابات", + "mentions": "الإشارات", + "message_sent": "تم إرسال الرسالة", + "search": "البحث", + "overview": "نظرة عامة", + "view": "اعرض", + "logout": "الخروج", + "login": "لِج", + "sign_up": "إنشاء حساب", + "messages": "لرسائل", + "password": "الكلمة السرية", + "verify_password": "تأكيد الكلمة السرية", + "forgot_password": "هل نسيت كلمتك السرية", + "new_password": "لكلمة السرية الجديدة", + "email": "البريد الإلكتروني", + "optional": "اختياري", + "community": "المجتمع", + "chat": "دردشة", + "recent_comments": "التعليقات الحديثة", + "no_results": "ليس هناك أية نتيجة.", + "setup": "التنصيب", + "your_site": "موقعك", + "modified": "تم تعديله", + "theme": "المظهر", + "donate_to_lemmy": "التبرّع إلى Lemmy", + "donate": "تبرع", + "joined": "عضو منذ", + "by": "مِن", + "to": "إلى", + "yes": "نعم", + "no": "لا", + "not_logged_in": "لستَ متصلا.", + "remove_comment": "احذف التعليق", + "create_a_community": "إنشاء مجتمع", + "create_private_message": "إنشاء رسالة خاصة", + "upload_avatar": "تحميل الصورة الرمزية", + "mods": "المُشرِفون", + "modlog": "تأريخ الإشراف", + "stickied": "تم تدبيسه", + "delete_account": "حذف الحساب", + "create": "إنشاء", + "email_or_username": "عنوان البريد أو اسم المستخدم", + "number_of_subscribers": "{{count}} مُتابِعين", + "unsubscribe": "إلغاء الإشتراك", + "week": "أسبوع", + "reply_sent": "تم إرسال الرد", + "login_sign_up": "لِج أو انشئ حسابا", + "old_password": "الكلمة السرية القديمة", + "matrix_user_id": "مستخدم ماتريكس", + "language": "اللغة", + "body": "المحتوى", + "setup_admin": "إنشاء مدير", + "are_you_sure": "هل أنت متأكّد؟" +} From 6c6e7aa872da32b21ec5dbc0342ccc51f616d109 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Sat, 29 Feb 2020 22:24:00 -0500 Subject: [PATCH 066/164] Version v0.6.28 --- ansible/VERSION | 2 +- docker/prod/docker-compose.yml | 2 +- server/src/version.rs | 2 +- ui/src/version.ts | 2 +- ui/translations/ar.json | 128 +++++++++++++++++++++++++++++++++ ui/translations/fr.json | 33 ++++----- 6 files changed, 144 insertions(+), 25 deletions(-) create mode 100644 ui/translations/ar.json diff --git a/ansible/VERSION b/ansible/VERSION index 898f3498c..50522d804 100644 --- a/ansible/VERSION +++ b/ansible/VERSION @@ -1 +1 @@ -v0.6.27 +v0.6.28 diff --git a/docker/prod/docker-compose.yml b/docker/prod/docker-compose.yml index 0cdcbe060..5b88fdf03 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.27 + image: dessalines/lemmy:v0.6.28 ports: - "127.0.0.1:8536:8536" restart: always diff --git a/server/src/version.rs b/server/src/version.rs index a7c04a1f6..596622e21 100644 --- a/server/src/version.rs +++ b/server/src/version.rs @@ -1 +1 @@ -pub const VERSION: &str = "v0.6.27"; +pub const VERSION: &str = "v0.6.28"; diff --git a/ui/src/version.ts b/ui/src/version.ts index f798adf28..106643971 100644 --- a/ui/src/version.ts +++ b/ui/src/version.ts @@ -1 +1 @@ -export const version: string = 'v0.6.27'; +export const version: string = 'v0.6.28'; diff --git a/ui/translations/ar.json b/ui/translations/ar.json new file mode 100644 index 000000000..d47561395 --- /dev/null +++ b/ui/translations/ar.json @@ -0,0 +1,128 @@ +{ + "creator": "المؤلّف", + "username": "إسم المستخدم", + "post": "منشور", + "remove_post": "احذف المنشور", + "no_posts": "ليس هناك منشورات.", + "create_a_post": "أنشئ منشورا", + "create_post": "إنشاء منشور", + "posts": "منشورات", + "comments": "التعليقات", + "number_of_posts": "{{count}} منشورات", + "related_posts": "يمكن لهذه المنشورات أن تكون ذات صلة", + "communities": "المجتمعات", + "users": "المستخدِمون", + "create_community": "إنشاء مجتمع", + "remove_community": "حذف المجتمع", + "list_of_communities": "قائمة المجتمعات", + "send_secure_message": "ارسل الرسالة المؤمنة", + "send_message": "أرسل الرسالة", + "message": "رسالة", + "edit": "تعديل", + "reply": "رد", + "cancel": "إلغاء", + "preview": "معاينة", + "upload_image": "إرسال صورة", + "avatar": "الصورة الرمزية", + "show_avatars": "إظهار الصور الرمزية", + "view_source": "عرض المصدر", + "sticky": "تدبيس", + "unsticky": "فك التدبيس", + "link": "الرابط", + "mod": "مشرِف", + "moderates": "إشراف", + "settings": "الإعدادات", + "remove_as_mod": "إزالة كمُشرِف", + "appoint_as_mod": "تعيين كمُشرِف", + "admin": "مدير", + "admins": "المدراء", + "remove_as_admin": "إزالة كمدير", + "appoint_as_admin": "تعيين كمدير", + "remove": "إزالة", + "removed": "تمت إزالته", + "reason": "السبب", + "mark_as_read": "تعيين كمقروء", + "mark_as_unread": "تعيين كغير مقروء بعد", + "delete": "حذف", + "deleted": "تم حذفه", + "restore": "استعادة", + "ban": "طرد", + "ban_from_site": "طرده مِن الموقع", + "banned": "مطرود", + "save": "حفظ", + "number_of_users": "{{count}} مستخدِمين", + "number_of_points": "{{count}} نقاط", + "number_online": "{{count}} مستخدمين متّصلين", + "name": "الإسم", + "title": "العنوان", + "category": "الفئة", + "subscribers": "المتابِعون", + "both": "كلاهما", + "saved": "تم حفظه", + "subscribe": "اتبع", + "prev": "السابقة", + "next": "التالية", + "sidebar": "الشريط الجانبي", + "sort_type": "ترتيب حسب", + "hot": "المتداولة", + "new": "جديد", + "old": "قديم", + "month": "شهر", + "year": "سنة", + "all": "الكل", + "docs": "الدليل", + "type": "النوع", + "replies": "الإجابات", + "mentions": "الإشارات", + "message_sent": "تم إرسال الرسالة", + "search": "البحث", + "overview": "نظرة عامة", + "view": "اعرض", + "logout": "الخروج", + "login": "لِج", + "sign_up": "إنشاء حساب", + "messages": "لرسائل", + "password": "الكلمة السرية", + "verify_password": "تأكيد الكلمة السرية", + "forgot_password": "هل نسيت كلمتك السرية", + "new_password": "لكلمة السرية الجديدة", + "email": "البريد الإلكتروني", + "optional": "اختياري", + "community": "المجتمع", + "chat": "دردشة", + "recent_comments": "التعليقات الحديثة", + "no_results": "ليس هناك أية نتيجة.", + "setup": "التنصيب", + "your_site": "موقعك", + "modified": "تم تعديله", + "theme": "المظهر", + "donate_to_lemmy": "التبرّع إلى Lemmy", + "donate": "تبرع", + "joined": "عضو منذ", + "by": "مِن", + "to": "إلى", + "yes": "نعم", + "no": "لا", + "not_logged_in": "لستَ متصلا.", + "remove_comment": "احذف التعليق", + "create_a_community": "إنشاء مجتمع", + "create_private_message": "إنشاء رسالة خاصة", + "upload_avatar": "تحميل الصورة الرمزية", + "mods": "المُشرِفون", + "modlog": "تأريخ الإشراف", + "stickied": "تم تدبيسه", + "delete_account": "حذف الحساب", + "create": "إنشاء", + "email_or_username": "عنوان البريد أو اسم المستخدم", + "number_of_subscribers": "{{count}} مُتابِعين", + "unsubscribe": "إلغاء الإشتراك", + "week": "أسبوع", + "reply_sent": "تم إرسال الرد", + "login_sign_up": "لِج أو انشئ حسابا", + "old_password": "الكلمة السرية القديمة", + "matrix_user_id": "مستخدم ماتريكس", + "language": "اللغة", + "body": "المحتوى", + "setup_admin": "إنشاء مدير", + "are_you_sure": "هل أنت متأكّد؟" +} diff --git a/ui/translations/fr.json b/ui/translations/fr.json index 5f07d73ae..7f61cbfee 100644 --- a/ui/translations/fr.json +++ b/ui/translations/fr.json @@ -9,7 +9,7 @@ "related_posts": "Ces sujets peuvent être corrélés", "cross_posts": "Ce sujet a également été posté sur :", "cross_post": "publication croisée", - "cross_posted_to": "publication croisée à", + "cross_posted_to": "publication croisée à : ", "comments": "Commentaires", "number_of_comments": "{{count}} Commentaires", "remove_comment": "Supprimer le commentaire", @@ -64,8 +64,7 @@ "delete": "supprimer", "deleted": "supprimé", "delete_account": "Supprimer le compte", - "delete_account_confirm": - "Attention: cette action supprime toutes vos données de façons permanente ! Entrez votre mot de passe pour confirmer.", + "delete_account_confirm": "Avertissement : cette action supprimera toutes vos données de façons permanente ! Saisissez votre mot de passe pour confirmer.", "restore": "restaurer", "ban": "bannir", "ban_from_site": "bannir du site", @@ -122,8 +121,7 @@ "login_sign_up": "Se connecter / S'inscrire", "login": "Se connecter", "sign_up": "S'inscrire", - "notifications_error": - "Les notifications de bureau ne sont pas discponibles sur votre navigateur. Essayez Firefox ou Chrome.", + "notifications_error": "Les notifications de bureau ne sont pas discponibles sur votre navigateur. Essayez Firefox ou Chrome.", "unread_messages": "Messages non-lu", "messages": "Messages", "password": "Mot de passe", @@ -136,8 +134,7 @@ "no_email_setup": "Ce serveur n'a pas correctement configuré la messagerie de email.", "email": "Email", "matrix_user_id": "Utilisateur Matrix", - "private_message_disclaimer": - "Attention: les messages privés en Matrix ne sont pas sécurisés. S'il vous plait, créer un compte de <1>Riot.im pour des messages sécurisés.", + "private_message_disclaimer": "Attention : les messages privés dans Lemmy ne sont pas sécurisés. Veuillez créer un compte sur <1>Riot.im pour pouvoir envoyer des messages sécurisés.", "send_notifications_to_email": "Envoyer des notifications par email", "optional": "Optionnel", "expires": "Expire", @@ -150,7 +147,7 @@ "enable_nsfw": "Activer NSFW", "url": "URL", "body": "Texte", - "copy_suggested_title": "Ajouter le titre suggéré: {{title}}", + "copy_suggested_title": "copier le titre suggéré : {{title}}", "community": "Communauté", "expand_here": "Développer ici", "subscribe_to_communities": "S'abonner à quelques <1>communautés.", @@ -167,8 +164,7 @@ "theme": "Thème", "sponsors": "Sponsors", "sponsors_of_lemmy": "Sponsors de Lemmy", - "sponsor_message": - "Lemmy est gratuit et <1>open-source, c'est à dire sans publicité et sans monétisation. Pour toujours. Vos dons soutiennent directement le développement du projet. Merci à nos soutiens.", + "sponsor_message": "Lemmy est un logiciel libre et <1>open-source, c’est à dire, il fonctionne sans publicité et sans monétisation aucune. Vos dons soutiennent directement le développement du projet à temps plein. Merci à toutes ces personnes :", "support_on_patreon": "Soutenir sur Patreon", "support_on_liberapay": "Soutenir sur Liberapay", "donate_to_lemmy": "Faire un don à Lemmy", @@ -189,8 +185,7 @@ "yes": "oui", "no": "non", "powered_by": "Propulsé par", - "landing_0": - "Lemmy est un <1>aggrégateur de lien, similaire à reddit et conçu pour fonctionner sur le <2>fédiverse.<3>Il est auto-hébergeable, se met à jour en direct et est léger (<4>~80kB). La fédération via Activitypub est prévue. <5>Lemmy est une <6>version beta très précoce, et de nombreuses fonctionnalités sont manquantes ou non fonctionnelles. <7>Vous pouvez rapporter des bugs et suggérez de nouvelles fonctionnalités <8>ici.<9>Crée avec <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.", + "landing_0": "Lemmy est un <1>aggrégateur de lien, similaire à reddit et conçu pour fonctionner sur le <2>fédiverse.<3>Il est auto-hébergeable, se met à jour en direct et est léger (<4>~80kB). La fédération via Activitypub est prévue. <5>Lemmy est une <6>version beta très précoce, et de nombreuses fonctionnalités sont manquantes ou non fonctionnelles. <7>Vous pouvez rapporter des bugs et suggérez de nouvelles fonctionnalités <8>ici.<9>Crée avec <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.", "not_logged_in": "Vous n'êtes pas connecté.", "logged_in": "Vous êtes connecté.", "community_ban": "Vous avez été banni de cette communauté.", @@ -200,11 +195,9 @@ "couldnt_update_comment": "Impossible de mettre à jour le commentaire.", "couldnt_save_comment": "Impossible de sauvegarder le commentaire.", "couldnt_get_comments": "Impossible de obtenir les commentaires.", - "no_comment_edit_allowed": - "Vous n'êtes pas autorisé à éditer ce commentaire.", + "no_comment_edit_allowed": "Vous n'êtes pas autorisé à éditer ce commentaire.", "no_post_edit_allowed": "Vous n'êtes pas autorisé à éditer sujet.", - "no_community_edit_allowed": - "Vous n'êtes pas autorisé à éditer cette communauté.", + "no_community_edit_allowed": "Vous n'êtes pas autorisé à éditer cette communauté.", "couldnt_find_community": "Impossible de trouver cette communauté.", "couldnt_update_community": "Impossible d'éditer cette communauté.", "community_already_exists": "Cette communauté existe déjà.", @@ -222,16 +215,14 @@ "not_an_admin": "Pas administrateur.", "site_already_exists": "Le site existe déjà.", "couldnt_update_site": "Impossible de mettre à jour le site.", - "couldnt_find_that_username_or_email": - "Impossible de trouver cet utilisateur ou cet email.", + "couldnt_find_that_username_or_email": "Impossible de trouver cet utilisateur ou cet email.", "password_incorrect": "Mot de passe incorrect.", "passwords_dont_match": "Les mots de passes ne correspondent pas..", "admin_already_created": "Désolé, il y a déjà un admin.", "user_already_exists": "L'utilisateur existe déjà.", - "email_already_exists": "L'email existe déjà", + "email_already_exists": "L’email existe déjà.", "couldnt_update_user": "Impossible de mettre à jour l'utilisateur.", - "system_err_login": - "Erreur système. Essayez de vous déconneter puis de vous reconnecter.", + "system_err_login": "Erreur système. Essayez de vous déconneter puis de vous reconnecter.", "couldnt_create_private_message": "Impossible de créer un message privé.", "no_private_message_edit_allowed": "Pas autorisé à modifier un message privé.", "couldnt_update_private_message": "Impossible de modifier un message privé.", From b5d3859adc5aab9ae08e87b1ef58f4cb68b64c43 Mon Sep 17 00:00:00 2001 From: ButterflyOfFire Date: Sun, 1 Mar 2020 09:12:27 +0000 Subject: [PATCH 067/164] Translated using Weblate (Arabic) Currently translated at 61.7% (142 of 230 strings) Translation: Lemmy/lemmy Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/ar/ --- ui/translations/ar.json | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/ui/translations/ar.json b/ui/translations/ar.json index d47561395..91a21d111 100644 --- a/ui/translations/ar.json +++ b/ui/translations/ar.json @@ -124,5 +124,21 @@ "language": "اللغة", "body": "المحتوى", "setup_admin": "إنشاء مدير", - "are_you_sure": "هل أنت متأكّد؟" + "are_you_sure": "هل أنت متأكّد؟", + "logged_in": "إنّك متّصل.", + "user_already_exists": "هذا المستخدِم موجود بالفعل.", + "number_of_communities": "{{count}} مجتمعات", + "subscribed": "مُتابِعون", + "url": "الرابط", + "nsfw": "محتوى حساس", + "couldnt_create_comment": "تعذّر إنشاءالتعليق.", + "site_ban": "لقد تم طردك مِن هذا الموقع", + "not_an_admin": "لستَ مديرا.", + "password_incorrect": "الكلمة السرية خاطئة.", + "passwords_dont_match": "الكلمات السرية غير متطابقة.", + "email_already_exists": "عنوان البريد الإلكتروني هذا موجود بالفعل.", + "time": "الساعة", + "more": "المزيد", + "community_ban": "لقد تم طردك مِن هذا المجتمع.", + "action": "الإجراء" } From 063b0e6f16a139d0498114a5d1c6f0d86e5156a8 Mon Sep 17 00:00:00 2001 From: ButterflyOfFire <42316180+BoFFire@users.noreply.github.com> Date: Sun, 1 Mar 2020 10:59:08 +0100 Subject: [PATCH 068/164] Update README.md Adding translation widget % --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 641bbe01c..29889a2d1 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![Build Status](https://travis-ci.org/dessalines/lemmy.svg?branch=master)](https://travis-ci.org/dessalines/lemmy) [![GitHub issues](https://img.shields.io/github/issues-raw/dessalines/lemmy.svg)](https://github.com/dessalines/lemmy/issues) [![Docker Pulls](https://img.shields.io/docker/pulls/dessalines/lemmy.svg)](https://cloud.docker.com/repository/docker/dessalines/lemmy/) +[![Translation status](http://weblate.yerbamate.dev/widgets/lemmy/-/lemmy/svg-badge.svg)](http://weblate.yerbamate.dev/engage/lemmy/?utm_source=widget) [![License](https://img.shields.io/github/license/dessalines/lemmy.svg)](LICENSE) ![GitHub stars](https://img.shields.io/github/stars/dessalines/lemmy?style=social)
    From 67d1c72a598b6d14b95a86db8abd20114bee05a4 Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Sun, 1 Mar 2020 11:38:11 +0000 Subject: [PATCH 069/164] Translated using Weblate (Japanese) Currently translated at 85.2% (196 of 230 strings) Translation: Lemmy/lemmy Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/ja/ --- ui/translations/ja.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ui/translations/ja.json b/ui/translations/ja.json index 517db5639..7f5718a39 100644 --- a/ui/translations/ja.json +++ b/ui/translations/ja.json @@ -183,7 +183,7 @@ "not_logged_in": "ログインしていません。", "logged_in": "ログインしました。", "community_ban": "このコミュニティへのアクセスを禁止されています。", - "site_ban": "サイトへのアクセスを禁止されています。", + "site_ban": "サイトへのアクセスを禁止されています", "couldnt_create_comment": "投稿を作成できませんでした。", "couldnt_find_community": "コミュニティが見付かりません。", "couldnt_find_post": "投稿が見付かりません。", @@ -191,7 +191,8 @@ "password_incorrect": "パスワードが不正です。", "passwords_dont_match": "パスワードが一致しません。", "admin_already_created": "済みませんが、既に管理者が設定されています。", - "email_already_exists": "メールアドレスが既に使用されています", + "email_already_exists": "メールアドレスが既に使用されています。", "time": "時刻", - "action": "行動" + "action": "行動", + "more": "さらに表示" } From d71145bc6e881d972064aabcc071260f99505658 Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Sun, 1 Mar 2020 19:29:20 +0000 Subject: [PATCH 070/164] remove utm parameter --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 29889a2d1..2adad59c7 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![Build Status](https://travis-ci.org/dessalines/lemmy.svg?branch=master)](https://travis-ci.org/dessalines/lemmy) [![GitHub issues](https://img.shields.io/github/issues-raw/dessalines/lemmy.svg)](https://github.com/dessalines/lemmy/issues) [![Docker Pulls](https://img.shields.io/docker/pulls/dessalines/lemmy.svg)](https://cloud.docker.com/repository/docker/dessalines/lemmy/) -[![Translation status](http://weblate.yerbamate.dev/widgets/lemmy/-/lemmy/svg-badge.svg)](http://weblate.yerbamate.dev/engage/lemmy/?utm_source=widget) +[![Translation status](http://weblate.yerbamate.dev/widgets/lemmy/-/lemmy/svg-badge.svg)](http://weblate.yerbamate.dev/engage/lemmy/) [![License](https://img.shields.io/github/license/dessalines/lemmy.svg)](LICENSE) ![GitHub stars](https://img.shields.io/github/stars/dessalines/lemmy?style=social)
    From 76b10d01ff7e266d979e7bae75dadab30bf65d4d Mon Sep 17 00:00:00 2001 From: Dessalines Date: Sun, 1 Mar 2020 18:19:48 -0500 Subject: [PATCH 071/164] Changing mobile columns. --- ui/src/components/post-listing.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index 519c7f740..fe4e65193 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -268,7 +268,7 @@ export class PostListing extends Component { )}
    {!this.state.imageExpanded && ( -
    +
    {this.thumbnail()}
    )} @@ -285,7 +285,7 @@ export class PostListing extends Component { )} -
    +
    From 9c50d5f073661a189fadfa3f09450462f94fe6b6 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Sun, 1 Mar 2020 18:21:52 -0500 Subject: [PATCH 072/164] Fix weblate deploy. --- docker/dev/deploy.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docker/dev/deploy.sh b/docker/dev/deploy.sh index 94a2fa91e..db2294fab 100755 --- a/docker/dev/deploy.sh +++ b/docker/dev/deploy.sh @@ -2,10 +2,8 @@ git checkout master # Import translations -wget "https://weblate.yerbamate.dev/download/lemmy/lemmy/?format=zip" -O /tmp/lemmy_l10n.zip -unzip -j -o /tmp/lemmy_l10n.zip -d ../../ui/translations/ -git add ../../ui/translations -rm /tmp/lemmy_l10n.zip +git fetch weblate +git merge weblate/master # Creating the new tag new_tag="$1" From 80f9003135f65f4f63a9a7df2da0954abe6f4790 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Sun, 1 Mar 2020 18:26:17 -0500 Subject: [PATCH 073/164] Version v0.6.29 --- ansible/VERSION | 2 +- docker/prod/docker-compose.yml | 2 +- server/src/version.rs | 2 +- ui/src/version.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ansible/VERSION b/ansible/VERSION index 50522d804..49d70ca7e 100644 --- a/ansible/VERSION +++ b/ansible/VERSION @@ -1 +1 @@ -v0.6.28 +v0.6.29 diff --git a/docker/prod/docker-compose.yml b/docker/prod/docker-compose.yml index 5b88fdf03..53de5d088 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.28 + image: dessalines/lemmy:v0.6.29 ports: - "127.0.0.1:8536:8536" restart: always diff --git a/server/src/version.rs b/server/src/version.rs index 596622e21..2c69014cb 100644 --- a/server/src/version.rs +++ b/server/src/version.rs @@ -1 +1 @@ -pub const VERSION: &str = "v0.6.28"; +pub const VERSION: &str = "v0.6.29"; diff --git a/ui/src/version.ts b/ui/src/version.ts index 106643971..64c6affae 100644 --- a/ui/src/version.ts +++ b/ui/src/version.ts @@ -1 +1 @@ -export const version: string = 'v0.6.28'; +export const version: string = 'v0.6.29'; From 0bf4a6a6b515a3326dd12b9eb8063eea7988aaec Mon Sep 17 00:00:00 2001 From: Dessalines Date: Mon, 2 Mar 2020 10:48:06 -0500 Subject: [PATCH 074/164] Fix image expand width on mobile. --- ui/src/components/post-listing.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index fe4e65193..732664e88 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -285,7 +285,9 @@ export class PostListing extends Component { )} -
    +
    From a8303940dfb24d56bc1be4dd2b440f1923665d9b Mon Sep 17 00:00:00 2001 From: Dessalines Date: Mon, 2 Mar 2020 11:00:34 -0500 Subject: [PATCH 075/164] Upgrading inferno. --- ui/package.json | 4 ++-- ui/yarn.lock | 31 +++++++++++++++++++++++++------ 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/ui/package.json b/ui/package.json index f0e9f0b72..f12f947af 100644 --- a/ui/package.json +++ b/ui/package.json @@ -27,9 +27,9 @@ "emoji-short-name": "^1.0.0", "husky": "^4.2.1", "i18next": "^19.0.3", - "inferno": "^7.0.1", + "inferno": "^7.4.2", "inferno-i18next": "nimbusec-oss/inferno-i18next", - "inferno-router": "^7.0.1", + "inferno-router": "^7.4.2", "js-cookie": "^2.2.0", "jwt-decode": "^2.2.0", "markdown-it": "^10.0.0", diff --git a/ui/yarn.lock b/ui/yarn.lock index 441c9d4a9..601f3067a 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -2229,14 +2229,14 @@ inferno-i18next@nimbusec-oss/inferno-i18next: inferno-shared "^7.1.12" inferno-vnode-flags "^7.1.12" -inferno-router@^7.0.1: - version "7.4.0" - resolved "https://registry.yarnpkg.com/inferno-router/-/inferno-router-7.4.0.tgz#0af6b931c58f426d0d7e7754d51a51300882364a" - integrity sha512-6Q76UjAiPd1mO/5sbDaEoEN9MdMHKkEXnYNOZ02sSudj5jWCFzJ/JnSF526uNxAHQpw2DKCh2pNiu6qf/b1vQQ== +inferno-router@^7.4.2: + version "7.4.2" + resolved "https://registry.yarnpkg.com/inferno-router/-/inferno-router-7.4.2.tgz#2e45f382f0c6adc1947da4c6c517f7e89d82f165" + integrity sha512-3iNGhp/x16XXD9+/WCLEWLC0dH6UUl+ZKk14gPhrVJQdeqfV387aaVw9Y+YpHZZWDRkqIL9MNyHHK27QL8gY/A== dependencies: history "^4.10.1" hoist-non-inferno-statics "^1.1.3" - inferno "7.4.0" + inferno "7.4.2" path-to-regexp-es6 "1.7.0" inferno-shared@7.4.0, inferno-shared@^7.1.12: @@ -2244,12 +2244,22 @@ inferno-shared@7.4.0, inferno-shared@^7.1.12: resolved "https://registry.yarnpkg.com/inferno-shared/-/inferno-shared-7.4.0.tgz#4491deb75348019939b160cd5655196afa13ced0" integrity sha512-6aa1fC/e4SP2lOLNg4ZS5Zz2SC+DnM7WxQbggmHhLSyOqZrsPrpZSlX25LbjR9lkhMrq6cmki3yInYFGuDzlRg== +inferno-shared@7.4.2: + version "7.4.2" + resolved "https://registry.yarnpkg.com/inferno-shared/-/inferno-shared-7.4.2.tgz#400cf6d19a077af9e5e852e1f189726391efa273" + integrity sha512-SULfgzj/PuyMd3rHThEXkeEdZorjcr/q+kaqbwr7C2XhIPCk4e5jcRKZujI6YCSahGA9WFwP2Mft1v5PsaaNeg== + inferno-vnode-flags@7.4.0, inferno-vnode-flags@^7.1.12: version "7.4.0" resolved "https://registry.yarnpkg.com/inferno-vnode-flags/-/inferno-vnode-flags-7.4.0.tgz#5c049a73f3ff84a51458b06d279d6b18d09acdf0" integrity sha512-TMPrvAxR2uUVSowLKnGgH34eWXErIYCdJ4d5hj8cSc8ta8knN6dj0z47UIw13qvmWfNjHgwm0C2/cm+G6fckiA== -inferno@7.4.0, inferno@^7.0.1, inferno@^7.1.12: +inferno-vnode-flags@7.4.2: + version "7.4.2" + resolved "https://registry.yarnpkg.com/inferno-vnode-flags/-/inferno-vnode-flags-7.4.2.tgz#54982dabe34f308853ba17de7de4241e23769135" + integrity sha512-sV4KqqvZH4MW9/dNbC9blHInnpQSqMWouU5VlanbJ+NhJ8ufPwsDy0/+jiA2aODpg2HFHwVMJFF1fsewPqNtLQ== + +inferno@7.4.0, inferno@^7.1.12: version "7.4.0" resolved "https://registry.yarnpkg.com/inferno/-/inferno-7.4.0.tgz#8d3dc03562c6851043a1a467fd509f222e9dbf85" integrity sha512-oEXx5iQmGXOvAPj1TZyCo6ndOc4qPg9zBLigMpkApAiV1SM/bri0M1eA/kD3e9jptcof9TwLBJD9bL6E6tq2tg== @@ -2258,6 +2268,15 @@ inferno@7.4.0, inferno@^7.0.1, inferno@^7.1.12: inferno-vnode-flags "7.4.0" opencollective-postinstall "^2.0.2" +inferno@7.4.2, inferno@^7.4.2: + version "7.4.2" + resolved "https://registry.yarnpkg.com/inferno/-/inferno-7.4.2.tgz#833cc423ee7b939fad705c59ea41924f31c92453" + integrity sha512-RsmuN8F7lAWTTuy2juf3Tqn/BihkRNdy0WZN+vuyryuEySKHBA1fruyq6K0covF3Ja8mAdha5NNISZz9ltgcug== + dependencies: + inferno-shared "7.4.2" + inferno-vnode-flags "7.4.2" + opencollective-postinstall "^2.0.2" + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" From 5f6f51b549d42943b85d8f7dc9d193aec0935ab6 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Mon, 2 Mar 2020 11:01:00 -0500 Subject: [PATCH 076/164] Moving link out of more menu. --- ui/src/components/comment-node.tsx | 65 ++++++++++++++++-------------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index a904eae36..cd95a75be 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -270,28 +270,6 @@ export class CommentNode extends Component { {node.comment.saved ? i18n.t('unsave') : i18n.t('save')} - {this.myComment && ( - <> -
  • - - {i18n.t('edit')} - -
  • -
  • - - {!node.comment.deleted - ? i18n.t('delete') - : i18n.t('restore')} - -
  • - - )} {!this.myComment && (
  • {
  • )} +
  • + + {i18n.t('link')} + +
  • {!this.state.showAdvanced ? (
  • { {i18n.t('view_source')}
  • -
  • - - {i18n.t('link')} - -
  • +
  • + {this.myComment && ( + <> +
  • + + {i18n.t('edit')} + +
  • +
  • + + {!node.comment.deleted + ? i18n.t('delete') + : i18n.t('restore')} + +
  • + + )} {/* Admins and mods can remove comments */} {(this.canMod || this.canAdmin) && ( <> -
  • {!node.comment.removed ? ( Date: Tue, 3 Mar 2020 02:29:45 -0500 Subject: [PATCH 077/164] A first pass at adding icons, and tippy tooltips. - Adding icons for post-listing, comment-node, and navbar. - Adding html titles. - Updating moment expand to use users locale. --- ui/assets/css/main.css | 26 +++++ ui/assets/css/tippy.css | 1 + ui/package.json | 1 + ui/src/components/comment-form.tsx | 14 ++- ui/src/components/comment-node.tsx | 141 ++++++++++++++++++++-------- ui/src/components/community.tsx | 1 + ui/src/components/iframely-card.tsx | 2 + ui/src/components/inbox.tsx | 1 + ui/src/components/main.tsx | 2 + ui/src/components/moment-time.tsx | 27 +++++- ui/src/components/navbar.tsx | 32 +++++-- ui/src/components/post-form.tsx | 16 +++- ui/src/components/post-listing.tsx | 133 +++++++++++++++++++------- ui/src/components/symbols.tsx | 57 +++++++---- ui/src/components/user.tsx | 1 + ui/src/index.html | 1 + ui/src/utils.ts | 12 +++ ui/translations/en.json | 2 + ui/yarn.lock | 12 +++ 19 files changed, 373 insertions(+), 109 deletions(-) create mode 100644 ui/assets/css/tippy.css diff --git a/ui/assets/css/main.css b/ui/assets/css/main.css index b03f27036..d206a508d 100644 --- a/ui/assets/css/main.css +++ b/ui/assets/css/main.css @@ -95,8 +95,17 @@ fill: currentColor; vertical-align: middle; align-self: center; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } +.icon-inline { + margin-bottom: 2px; +} .spin { animation: spins 2s linear infinite; @@ -225,3 +234,20 @@ hr { height: 50px; width: 50px; } + +.unselectable { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.list-inline-item-action { + display: inline-block; +} + +.list-inline-item-action:not(:last-child) { + margin-right: 1.2rem; +} diff --git a/ui/assets/css/tippy.css b/ui/assets/css/tippy.css new file mode 100644 index 000000000..ff0a31326 --- /dev/null +++ b/ui/assets/css/tippy.css @@ -0,0 +1 @@ +.tippy-box[data-animation=fade][data-state=hidden]{opacity:0}.tippy-iOS{cursor:pointer!important;-webkit-tap-highlight-color:transparent}[data-tippy-root]{max-width:calc(100vw - 10px)}.tippy-box{position:relative;background-color:#333;color:#fff;border-radius:4px;font-size:14px;line-height:1.4;outline:0;transition-property:transform,visibility,opacity}.tippy-box[data-placement^=top]>.tippy-arrow{bottom:0}.tippy-box[data-placement^=top]>.tippy-arrow:before{border-width:8px 8px 0;border-top-color:#333;bottom:-7px;transform-origin:center top}.tippy-box[data-placement^=bottom]>.tippy-arrow{top:0}.tippy-box[data-placement^=bottom]>.tippy-arrow:before{top:-7px;border-width:0 8px 8px;border-bottom-color:#333;transform-origin:center bottom}.tippy-box[data-placement^=left]>.tippy-arrow{right:0}.tippy-box[data-placement^=left]>.tippy-arrow:before{border-width:8px 0 8px 8px;border-left-color:#333;right:-7px;transform-origin:center left}.tippy-box[data-placement^=right]>.tippy-arrow{left:0}.tippy-box[data-placement^=right]>.tippy-arrow:before{left:-7px;border-width:8px 8px 8px 0;border-right-color:#333;transform-origin:center right}.tippy-box[data-inertia][data-state=visible]{transition-timing-function:cubic-bezier(.54,1.5,.38,1.11)}.tippy-arrow{width:16px;height:16px}.tippy-arrow:before{content:"";position:absolute;border-color:transparent;border-style:solid}.tippy-content{position:relative;padding:5px 9px;z-index:1} \ No newline at end of file diff --git a/ui/package.json b/ui/package.json index f12f947af..8658d1363 100644 --- a/ui/package.json +++ b/ui/package.json @@ -41,6 +41,7 @@ "reconnecting-websocket": "^4.4.0", "rxjs": "^6.4.0", "terser": "^4.6.3", + "tippy.js": "^6.0.0", "toastify-js": "^1.6.2", "tributejs": "^4.1.1", "twemoji": "^12.1.2", diff --git a/ui/src/components/comment-form.tsx b/ui/src/components/comment-form.tsx index eaa054d83..aa8e651de 100644 --- a/ui/src/components/comment-form.tsx +++ b/ui/src/components/comment-form.tsx @@ -141,16 +141,22 @@ export class CommentForm extends Component { - {i18n.t('formatting_help')} + + + - + { this.handleCommentDownvote = this.handleCommentDownvote.bind(this); } + componentDidUpdate() { + setupTippy(); + } + componentWillReceiveProps(nextProps: CommentNodeProps) { this.state.my_vote = nextProps.node.comment.my_vote; this.state.upvotes = nextProps.node.comment.upvotes; @@ -128,18 +133,22 @@ export class CommentNode extends Component { this.state.my_vote == 1 ? 'text-info' : 'text-muted' }`} onClick={linkEvent(node, this.handleCommentUpvote)} + data-tippy-content={i18n.t('upvote')} > -
    {this.state.score}
    +
    + {this.state.score} +
    {WebSocketService.Instance.site.enable_downvotes && (
  • )}
  • - - (+{this.state.upvotes} - | - -{this.state.downvotes} - ) + + + + + {this.state.upvotes} + +
  • +
  • + + + + + {this.state.downvotes}
  • {this.props.showCommunity && ( @@ -214,7 +231,7 @@ export class CommentNode extends Component {
  • {this.state.collapsed ? '[+]' : '[-]'} @@ -239,97 +256,141 @@ export class CommentNode extends Component { dangerouslySetInnerHTML={mdToHtml(this.commentUnlessRemoved)} /> )} -
  • diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index 732664e88..5f3fef09e 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -30,6 +30,7 @@ import { pictshareAvatarThumbnail, showAvatars, imageThumbnailer, + setupTippy, } from '../utils'; import { i18n } from '../i18next'; @@ -101,6 +102,10 @@ export class PostListing extends Component { } } + componentDidUpdate() { + setupTippy(); + } + componentWillReceiveProps(nextProps: PostListingProps) { this.state.my_vote = nextProps.post.my_vote; this.state.upvotes = nextProps.post.upvotes; @@ -185,7 +190,7 @@ export class PostListing extends Component { return ( {this.imgThumb()} @@ -246,12 +251,13 @@ export class PostListing extends Component { this.state.my_vote == 1 ? 'text-info' : 'text-muted' }`} onClick={linkEvent(this, this.handlePostLike)} + data-tippy-content={i18n.t('upvote')} > -
    +
    {this.state.score}
    {WebSocketService.Instance.site.enable_downvotes && ( @@ -260,6 +266,7 @@ export class PostListing extends Component { this.state.my_vote == -1 ? 'text-danger' : 'text-muted' }`} onClick={linkEvent(this, this.handlePostDisLike)} + data-tippy-content={i18n.t('downvote')} > @@ -333,8 +340,8 @@ export class PostListing extends Component { <> {!this.state.imageExpanded ? ( [+] @@ -342,7 +349,7 @@ export class PostListing extends Component { ) : ( [-] @@ -439,22 +446,37 @@ export class PostListing extends Component {
  • - - (+{this.state.upvotes} - | - -{this.state.downvotes} - ) + + + + + {this.state.upvotes}
  • - - {i18n.t('number_of_comments', { + + + + + {this.state.downvotes} + +
  • +
  • + + + + + {post.number_of_comments}
  • -
      +
        {this.props.post.duplicates && ( <>
      • @@ -470,65 +492,90 @@ export class PostListing extends Component { )}
      -
        +
          {UserService.Instance.user && ( <> {this.props.showBody && ( <> -
        • +
        • - {post.saved ? i18n.t('unsave') : i18n.t('save')} + + +
        • -
        • +
        • - {i18n.t('cross_post')} + + +
        • )} {this.myPost && this.props.showBody && ( <> -
        • +
        • - {i18n.t('edit')} + + +
        • -
        • +
        • - {!post.deleted - ? i18n.t('delete') - : i18n.t('restore')} + + +
        • )} {!this.state.showAdvanced && this.props.showBody ? ( -
        • +
        • - {i18n.t('more')} + + +
        • ) : ( <> {this.props.showBody && post.body && ( -
        • +
        • { )} {this.canModOnSelf && ( <> -
        • +
        • - {post.locked - ? i18n.t('unlock') - : i18n.t('lock')} + + +
        • -
        • +
        • - {post.stickied - ? i18n.t('unsticky') - : i18n.t('sticky')} + + +
        • diff --git a/ui/src/components/symbols.tsx b/ui/src/components/symbols.tsx index b56732632..09efcf68f 100644 --- a/ui/src/components/symbols.tsx +++ b/ui/src/components/symbols.tsx @@ -15,40 +15,66 @@ export class Symbols extends Component { xmlnsXlink="http://www.w3.org/1999/xlink" > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - message-square - - image - - - + + - external-link - coffee1 - rss - arrow-down - arrow-up - - mail - + + { - search - github - spinner diff --git a/ui/src/components/user.tsx b/ui/src/components/user.tsx index e2df15e14..e97d7b08d 100644 --- a/ui/src/components/user.tsx +++ b/ui/src/components/user.tsx @@ -267,6 +267,7 @@ export class User extends Component { SortType[this.state.sort] }`} target="_blank" + title="RSS" > # diff --git a/ui/src/index.html b/ui/src/index.html index 24bed7247..f39773d02 100644 --- a/ui/src/index.html +++ b/ui/src/index.html @@ -14,6 +14,7 @@ + diff --git a/ui/src/utils.ts b/ui/src/utils.ts index 661057206..7ef3e303e 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -41,6 +41,7 @@ import markdown_it_container from 'markdown-it-container'; import twemoji from 'twemoji'; import emojiShortName from 'emoji-short-name'; import Toastify from 'toastify-js'; +import tippy from 'tippy.js'; export const repoUrl = 'https://github.com/dessalines/lemmy'; export const markdownHelpUrl = '/docs/about_guide.html'; @@ -477,6 +478,17 @@ export function setupTribute(): Tribute { }); } +let tippyInstance = tippy('[data-tippy-content]'); + +export function setupTippy() { + tippyInstance.forEach(e => e.destroy()); + tippyInstance = tippy('[data-tippy-content]', { + delay: [500, 0], + // Display on "long press" + touch: ['hold', 500], + }); +} + function userSearch(text: string, cb: any) { if (text) { let form: SearchForm = { diff --git a/ui/translations/en.json b/ui/translations/en.json index 8b719c712..1f959396a 100644 --- a/ui/translations/en.json +++ b/ui/translations/en.json @@ -146,6 +146,8 @@ "browser_default": "Browser Default", "downvotes_disabled": "Downvotes disabled", "enable_downvotes": "Enable Downvotes", + "upvote": "Upvote", + "downvote": "Downvote", "open_registration": "Open Registration", "registration_closed": "Registration closed", "enable_nsfw": "Enable NSFW", diff --git a/ui/yarn.lock b/ui/yarn.lock index 601f3067a..6c1f222b2 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -104,6 +104,11 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" +"@popperjs/core@^2.0.6": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.0.6.tgz#5a39ac118811ca844155b0ad5190b8c24f35e533" + integrity sha512-zj7Gw8QC4jmR92eKUvtrZUEpl2ypRbq+qlE4pwf9n2hnUO9BOAcWUs4/Ht+gNIbFt98xtqhLvccdCfD469MzpQ== + "@samverschueren/stream-to-observable@^0.3.0": version "0.3.0" resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f" @@ -4432,6 +4437,13 @@ tiny-warning@^1.0.0: resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== +tippy.js@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-6.0.0.tgz#6bc4f477ea82ef344db51ae34f106a8b25f2d02c" + integrity sha512-2NVc5A8cnO9N/Fk+tTU6KEm6m8ZXT1u3pOY756zZ5BE38rWDl/hVyoWhTfM79HW1nEJSpn/VujqAMMZGHsE9qQ== + dependencies: + "@popperjs/core" "^2.0.6" + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" From 7d602c26f84e815da507eff754ce5e772ff0b207 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Tue, 3 Mar 2020 09:16:36 -0500 Subject: [PATCH 078/164] Some formatting. --- ui/src/components/post-listing.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index 5f3fef09e..bc23543a5 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -476,7 +476,7 @@ export class PostListing extends Component {
        -
          +
            {this.props.post.duplicates && ( <>
          • From ade334b2513242c5ab59f336a2f457c7379b46b9 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Tue, 3 Mar 2020 09:23:43 -0500 Subject: [PATCH 079/164] Add Lemmy Council governance document. --- docs/src/SUMMARY.md | 1 + docs/src/lemmy_council.md | 53 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 docs/src/lemmy_council.md diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 0514cbcda..9a2ecde8d 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -15,3 +15,4 @@ - [Local Development](contributing_local_development.md) - [Websocket/HTTP API](contributing_websocket_http_api.md) - [ActivityPub API Outline](contributing_apub_api_outline.md) +- [Lemmy Council](lemmy_council.md) diff --git a/docs/src/lemmy_council.md b/docs/src/lemmy_council.md new file mode 100644 index 000000000..96234ff93 --- /dev/null +++ b/docs/src/lemmy_council.md @@ -0,0 +1,53 @@ +# Lemmy Council + +- A group of lemmy developers and users that use a well-defined democratic process to steer the project in a positive direction, keep it aligned to community goals, and resolve conflicts. + +## Voting / Decision-Making + +### Process +- Anything is open for discussion +- Voting done through matrix chat reacts (thumbs up/thumbs down) +- Require a simple majority for votes. (Maybe 2/3rds for more debated decisions). +- Once a decision is reached democratically, the dicision is binding and all group members have to follow it +- All members of the Lemmy council have equal voting power. +- Voting must stay open for at least 2 days. + +### What gets voted on +- Membership (joining, removing) +- Coding direction + - Priorities / Emphasis + - Controversial features (For example, an unpopular feature should be removed) +- Communication mediums +- Conflict resolution +- dev.lemmy.ml (domain and server) +- lemmy.ml and subdomains (excluding communism.lemmy.ml) +- git repo including mirrors (on github, gitea, etc) +- Any official accounts of the Lemmy project, for example the Mastodon account or the Liberapay account +- Changes to these rules + +## Joining +- We use the following process: anyone who is active around Lemmy can recommend any other active person to join the council. This has to be approved by a majority of the council. +- Active users are defined as those who contribute to Lemmy in some way for at least an hour per week on average, doing things like reporting bugs, discussing rules and features, translating, promoting, developing, or doing other things that aim to improve Lemmy as a whole. + -> people should have joined at least a month ago. +- The member list is public. +- Note: we would like to have a process where community members can elect candidates for the council, but this is not realistic because a single user could easily create multiple accounts and cheat the vote. +- Limit growth to one new member per month at most. + +## Removing members +- Inactive members should be removed from the council after a few months of inactivity, and after receiving a notification about this. +- Members that dont follow binding council decisions should be removed. +- Any member can be removed in a vote. + +## Goals +- We encourage the membership of groups such as LGBT, religious or ethnic minorities, abuse victims, etc etc, and strive to create a safe space for them to express their opinions. We also support measures to increase participation by the previously mentioned groups. +- The following are banned, and will always be harshly punished: fascism, abuse, racism, sexism, etc etc, + +## Communication +- A private Matrix chat for all council members. +- (Once private communities are done) A private community on dev.lemmy.ml for issues. + +## Member List / Contact Info +General Contact [@LemmyDev Mastodon](https://mastodon.social/@LemmyDev) + +- Dessalines [Matrix](https://matrix.to/#/@happydooby:matrix.org) +- Nutomic [Matrix](https://matrix.to/#/@nutomic:matrix.org), [Mastodon](https://radical.town/@felix) From b3b4b7977570edda224003efca5396c10363599d Mon Sep 17 00:00:00 2001 From: Dessalines Date: Tue, 3 Mar 2020 10:14:50 -0500 Subject: [PATCH 080/164] More additions to icons. - Adding edit and trash icons for sidebars - Adding pencil edit instead of modified. --- ui/src/components/main.tsx | 9 ++++++--- ui/src/components/moment-time.tsx | 11 ++++++++--- ui/src/components/post-listing.tsx | 8 +++++++- ui/src/components/sidebar.tsx | 30 +++++++++++++++++++++++------- ui/src/components/symbols.tsx | 3 +++ 5 files changed, 47 insertions(+), 14 deletions(-) diff --git a/ui/src/components/main.tsx b/ui/src/components/main.tsx index 014d82d31..3f7d8fa6e 100644 --- a/ui/src/components/main.tsx +++ b/ui/src/components/main.tsx @@ -268,13 +268,16 @@ export class Main extends Component {
            {`${this.state.siteRes.site.name}`}
            {this.canAdmin && ( -
              -
            • +
                +
              • - {i18n.t('edit')} + + +
              diff --git a/ui/src/components/moment-time.tsx b/ui/src/components/moment-time.tsx index a256f7858..76e5fe289 100644 --- a/ui/src/components/moment-time.tsx +++ b/ui/src/components/moment-time.tsx @@ -1,6 +1,6 @@ import { Component } from 'inferno'; import moment from 'moment'; -import { getMomentLanguage, setupTippy } from '../utils'; +import { getMomentLanguage, setupTippy, capitalizeFirstLetter } from '../utils'; import { i18n } from '../i18next'; interface MomentTimeProps { @@ -28,10 +28,15 @@ export class MomentTime extends Component { if (this.props.data.updated) { return ( - {i18n.t('modified')} {moment.utc(this.props.data.updated).fromNow()} + + + + {moment.utc(this.props.data.updated).fromNow()} ); } else { diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index bc23543a5..6e285ee9f 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -579,8 +579,14 @@ export class PostListing extends Component { - {i18n.t('view_source')} + + +
            • )} diff --git a/ui/src/components/sidebar.tsx b/ui/src/components/sidebar.tsx index 5c8ef1a9a..a78589d55 100644 --- a/ui/src/components/sidebar.tsx +++ b/ui/src/components/sidebar.tsx @@ -13,6 +13,7 @@ import { getUnixTime, pictshareAvatarThumbnail, showAvatars, + setupTippy, } from '../utils'; import { CommunityForm } from './community-form'; import { i18n } from '../i18next'; @@ -46,6 +47,10 @@ export class Sidebar extends Component { this.handleEditCancel = this.handleEditCancel.bind(this); } + componentDidUpdate() { + setupTippy(); + } + render() { return (
              @@ -84,26 +89,37 @@ export class Sidebar extends Component { /c/{community.name} -
                +
                  {this.canMod && ( <> -
                • +
                • - {i18n.t('edit')} + + +
                • {this.amCreator && ( -
                • +
                • - {!community.deleted - ? i18n.t('delete') - : i18n.t('restore')} + + +
                • )} diff --git a/ui/src/components/symbols.tsx b/ui/src/components/symbols.tsx index 09efcf68f..108fbab39 100644 --- a/ui/src/components/symbols.tsx +++ b/ui/src/components/symbols.tsx @@ -42,6 +42,9 @@ export class Symbols extends Component { + + + From 48594537c038e7c57e232d8de0daa25c7007d44c Mon Sep 17 00:00:00 2001 From: Dessalines Date: Tue, 3 Mar 2020 11:07:07 -0500 Subject: [PATCH 081/164] Change action sizes. Add better tippy updating. --- ui/src/components/comment-node.tsx | 15 ++++++++++++--- ui/src/components/post-listing.tsx | 16 +++++++++++++--- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index 3db87aa58..4d95073c1 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -103,8 +103,16 @@ export class CommentNode extends Component { this.handleCommentDownvote = this.handleCommentDownvote.bind(this); } - componentDidUpdate() { - setupTippy(); + componentDidUpdate(prevProps: CommentNodeProps) { + let prevComment = prevProps.node.comment; + let comment = this.props.node.comment; + if ( + prevComment.saved !== comment.saved || + prevComment.deleted !== comment.deleted || + prevComment.read !== comment.read + ) { + setupTippy(); + } } componentWillReceiveProps(nextProps: CommentNodeProps) { @@ -256,7 +264,7 @@ export class CommentNode extends Component { dangerouslySetInnerHTML={mdToHtml(this.commentUnlessRemoved)} /> )} -
                    +
                      {this.props.markable && (
                    • { handleShowAdvanced(i: CommentNode) { i.state.showAdvanced = !i.state.showAdvanced; i.setState(i.state); + setupTippy(); } } diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index 6e285ee9f..97d319d81 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -102,8 +102,17 @@ export class PostListing extends Component { } } - componentDidUpdate() { - setupTippy(); + componentDidUpdate(prevProps: PostListingProps) { + let prevPost = prevProps.post; + let post = this.props.post; + if ( + prevPost.saved !== post.saved || + prevPost.deleted !== post.deleted || + prevPost.locked !== post.locked || + prevPost.stickied !== post.stickied + ) { + setupTippy(); + } } componentWillReceiveProps(nextProps: PostListingProps) { @@ -492,7 +501,7 @@ export class PostListing extends Component { )}
                    -
                      +
                        {UserService.Instance.user && ( <> {this.props.showBody && ( @@ -1305,5 +1314,6 @@ export class PostListing extends Component { handleShowAdvanced(i: PostListing) { i.state.showAdvanced = !i.state.showAdvanced; i.setState(i.state); + setupTippy(); } } From 0aec31328d0c75e08480b15a0e822e6ed4dd86a1 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Tue, 3 Mar 2020 11:46:29 -0500 Subject: [PATCH 082/164] Removing icon-inline from message icon. --- ui/src/components/comment-node.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index 4d95073c1..340ef1090 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -321,7 +321,7 @@ export class CommentNode extends Component { to={`/create_private_message?recipient_id=${node.comment.creator_id}`} title={i18n.t('message').toLowerCase()} > - + From a5331bce77d6cb04aa7af10007845a211da57d7c Mon Sep 17 00:00:00 2001 From: Dessalines Date: Tue, 3 Mar 2020 16:36:11 -0500 Subject: [PATCH 083/164] Only show tribute menu after a minlength of 3 characters. - Fixes #562 --- ui/package.json | 2 +- ui/src/utils.ts | 3 +++ ui/yarn.lock | 8 ++++---- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/ui/package.json b/ui/package.json index 8658d1363..e3cabae5b 100644 --- a/ui/package.json +++ b/ui/package.json @@ -43,7 +43,7 @@ "terser": "^4.6.3", "tippy.js": "^6.0.0", "toastify-js": "^1.6.2", - "tributejs": "^4.1.1", + "tributejs": "^5.0.0", "twemoji": "^12.1.2", "ws": "^7.0.0" }, diff --git a/ui/src/utils.ts b/ui/src/utils.ts index 7ef3e303e..d531a7ca9 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -446,6 +446,7 @@ export function setupTribute(): Tribute { allowSpaces: false, autocompleteMode: true, menuItemLimit: mentionDropdownFetchLimit, + menuShowMinLength: 3, }, // Users { @@ -459,6 +460,7 @@ export function setupTribute(): Tribute { allowSpaces: false, autocompleteMode: true, menuItemLimit: mentionDropdownFetchLimit, + menuShowMinLength: 3, }, // Communities @@ -473,6 +475,7 @@ export function setupTribute(): Tribute { allowSpaces: false, autocompleteMode: true, menuItemLimit: mentionDropdownFetchLimit, + menuShowMinLength: 3, }, ], }); diff --git a/ui/yarn.lock b/ui/yarn.lock index 6c1f222b2..d36bef04e 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -4506,10 +4506,10 @@ tough-cookie@~2.4.3: psl "^1.1.24" punycode "^1.4.1" -tributejs@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/tributejs/-/tributejs-4.1.1.tgz#f169a4ad12e485241140ec1ab987b460950c974c" - integrity sha512-jc+PcaiNzMjCn2LAQb3i4ic94EsSfLW8Jlk1sK2cb6hLcZFalU9ThcF8rxuKkTUKv1GIvTwN8XseLzCXLxB4lw== +tributejs@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/tributejs/-/tributejs-5.0.0.tgz#2c5301a79c19d7a72d23e995bf7c9f47c2a34f23" + integrity sha512-aPUpq4+NTXRq1OcdoeiFg9d+wM+J0b7dpL7MNVxqo8JIgChtkx8HnsPVl/uZ4Z1ChTF9UI1ffbvTfJRHqJjjAw== ts-node@^8.6.2: version "8.6.2" From 30165ebd15e560c4c4b00f6c6b7e1947f2652982 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Tue, 3 Mar 2020 17:37:27 -0500 Subject: [PATCH 084/164] Updating tributejs --- ui/src/utils.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ui/src/utils.ts b/ui/src/utils.ts index d531a7ca9..0376d3ce0 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -34,7 +34,7 @@ import { } from './interfaces'; import { UserService, WebSocketService } from './services'; -import Tribute from 'tributejs/src/Tribute.js'; +import Tribute from 'tributejs'; import markdown_it from 'markdown-it'; import markdownitEmoji from 'markdown-it-emoji/light'; import markdown_it_container from 'markdown-it-container'; @@ -427,7 +427,7 @@ export function toast(text: string, background: string = 'success') { }).showToast(); } -export function setupTribute(): Tribute { +export function setupTribute(): any { return new Tribute({ collection: [ // Emojis @@ -445,7 +445,7 @@ export function setupTribute(): Tribute { }), allowSpaces: false, autocompleteMode: true, - menuItemLimit: mentionDropdownFetchLimit, + // menuItemLimit: mentionDropdownFetchLimit, menuShowMinLength: 3, }, // Users @@ -459,7 +459,7 @@ export function setupTribute(): Tribute { }, allowSpaces: false, autocompleteMode: true, - menuItemLimit: mentionDropdownFetchLimit, + // menuItemLimit: mentionDropdownFetchLimit, menuShowMinLength: 3, }, @@ -474,7 +474,7 @@ export function setupTribute(): Tribute { }, allowSpaces: false, autocompleteMode: true, - menuItemLimit: mentionDropdownFetchLimit, + // menuItemLimit: mentionDropdownFetchLimit, menuShowMinLength: 3, }, ], From 107005c4748845df7f54439731337b78bdefd900 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Tue, 3 Mar 2020 19:14:36 -0500 Subject: [PATCH 085/164] Adding icon-plus and minus-square. --- ui/src/components/comment-node.tsx | 10 +++++++++- ui/src/components/post-listing.tsx | 8 ++++++-- ui/src/components/symbols.tsx | 6 ++++++ ui/src/utils.ts | 10 +++++----- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index 340ef1090..d548e9b21 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -242,7 +242,15 @@ export class CommentNode extends Component { className="unselectable pointer text-monospace" onClick={linkEvent(this, this.handleCommentCollapse)} > - {this.state.collapsed ? '[+]' : '[-]'} + {this.state.collapsed ? ( + + + + ) : ( + + + + )}
            diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index 97d319d81..193c2a549 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -353,7 +353,9 @@ export class PostListing extends Component { data-tippy-content={i18n.t('expand_here')} onClick={linkEvent(this, this.handleImageExpandClick)} > - [+] + + + ) : ( @@ -361,7 +363,9 @@ export class PostListing extends Component { class="text-monospace unselectable pointer ml-2 text-muted small" onClick={linkEvent(this, this.handleImageExpandClick)} > - [-] + + +
            { xmlnsXlink="http://www.w3.org/1999/xlink" > + + + + + + diff --git a/ui/src/utils.ts b/ui/src/utils.ts index 0376d3ce0..d531a7ca9 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -34,7 +34,7 @@ import { } from './interfaces'; import { UserService, WebSocketService } from './services'; -import Tribute from 'tributejs'; +import Tribute from 'tributejs/src/Tribute.js'; import markdown_it from 'markdown-it'; import markdownitEmoji from 'markdown-it-emoji/light'; import markdown_it_container from 'markdown-it-container'; @@ -427,7 +427,7 @@ export function toast(text: string, background: string = 'success') { }).showToast(); } -export function setupTribute(): any { +export function setupTribute(): Tribute { return new Tribute({ collection: [ // Emojis @@ -445,7 +445,7 @@ export function setupTribute(): any { }), allowSpaces: false, autocompleteMode: true, - // menuItemLimit: mentionDropdownFetchLimit, + menuItemLimit: mentionDropdownFetchLimit, menuShowMinLength: 3, }, // Users @@ -459,7 +459,7 @@ export function setupTribute(): any { }, allowSpaces: false, autocompleteMode: true, - // menuItemLimit: mentionDropdownFetchLimit, + menuItemLimit: mentionDropdownFetchLimit, menuShowMinLength: 3, }, @@ -474,7 +474,7 @@ export function setupTribute(): any { }, allowSpaces: false, autocompleteMode: true, - // menuItemLimit: mentionDropdownFetchLimit, + menuItemLimit: mentionDropdownFetchLimit, menuShowMinLength: 3, }, ], From 5b2383fa5050d30e5acf8399b78fd651ae0b4342 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 4 Mar 2020 09:30:49 -0500 Subject: [PATCH 086/164] Adding icons to stickied, deleted, and locked. --- ui/src/components/post-listing.tsx | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index 193c2a549..d702d78b0 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -339,7 +339,7 @@ export class PostListing extends Component { title={this.state.url} > {new URL(this.state.url).hostname} - + @@ -391,18 +391,33 @@ export class PostListing extends Component { )} {post.deleted && ( - - {i18n.t('deleted')} + + + + )} {post.locked && ( - - {i18n.t('locked')} + + + + )} {post.stickied && ( - - {i18n.t('stickied')} + + + + )} {post.nsfw && ( From d8bcfd7d44cd346727589515fdb314e45026c455 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 4 Mar 2020 09:37:19 -0500 Subject: [PATCH 087/164] Changing view source icon. --- ui/src/components/comment-node.tsx | 2 +- ui/src/components/post-listing.tsx | 2 +- ui/src/components/symbols.tsx | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index d548e9b21..ecf5e2aaf 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -370,7 +370,7 @@ export class CommentNode extends Component { class={`icon icon-inline ${this.state .viewSource && 'text-success'}`} > - +
          • diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index d702d78b0..b6cb3f511 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -613,7 +613,7 @@ export class PostListing extends Component { class={`icon icon-inline ${this.state .viewSource && 'text-success'}`} > - + diff --git a/ui/src/components/symbols.tsx b/ui/src/components/symbols.tsx index fcf75e30e..0c7169d53 100644 --- a/ui/src/components/symbols.tsx +++ b/ui/src/components/symbols.tsx @@ -42,6 +42,9 @@ export class Symbols extends Component { + + + From 8855b91d0c15bcf9273f81736149e74b8c1a2fe4 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 4 Mar 2020 09:42:50 -0500 Subject: [PATCH 088/164] Fixing select alignment. --- ui/src/components/community.tsx | 15 ++++++++------- ui/src/components/main.tsx | 16 +++++++++------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/ui/src/components/community.tsx b/ui/src/components/community.tsx index 1c875d04d..0be549a5a 100644 --- a/ui/src/components/community.tsx +++ b/ui/src/components/community.tsx @@ -194,13 +194,14 @@ export class Community extends Component { selects() { return ( -
            - - - +
            + + + + { selects() { return (
            - - + + + + { target="_blank" title="RSS" > - + # @@ -460,7 +462,7 @@ export class Main extends Component { target="_blank" title="RSS" > - + # From fb355188487bcd1e4185b8a034f95560cc28946d Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 4 Mar 2020 11:46:34 -0500 Subject: [PATCH 089/164] Fixing post sorting by stickied on front end. Fixes #575 --- ui/src/components/post-listings.tsx | 2 +- ui/src/utils.ts | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/ui/src/components/post-listings.tsx b/ui/src/components/post-listings.tsx index d61f624d4..5e6acc0ca 100644 --- a/ui/src/components/post-listings.tsx +++ b/ui/src/components/post-listings.tsx @@ -53,7 +53,7 @@ export class PostListings extends Component { } if (this.props.sort !== undefined) { - postSort(out, this.props.sort); + postSort(out, this.props.sort, this.props.showCommunity == undefined); } return out; diff --git a/ui/src/utils.ts b/ui/src/utils.ts index d531a7ca9..5987070cf 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -729,7 +729,11 @@ function convertCommentSortType(sort: SortType): CommentSortType { } } -export function postSort(posts: Array, sort: SortType) { +export function postSort( + posts: Array, + sort: SortType, + communityType: boolean +) { // First, put removed and deleted comments at the bottom, then do your other sorts if ( sort == SortType.TopAll || @@ -740,13 +744,17 @@ export function postSort(posts: Array, sort: SortType) { ) { posts.sort( (a, b) => - +a.removed - +b.removed || +a.deleted - +b.deleted || b.score - a.score + +a.removed - +b.removed || + +a.deleted - +b.deleted || + (communityType && +b.stickied - +a.stickied) || + b.score - a.score ); } else if (sort == SortType.New) { posts.sort( (a, b) => +a.removed - +b.removed || +a.deleted - +b.deleted || + (communityType && +b.stickied - +a.stickied) || b.published.localeCompare(a.published) ); } else if (sort == SortType.Hot) { @@ -754,6 +762,7 @@ export function postSort(posts: Array, sort: SortType) { (a, b) => +a.removed - +b.removed || +a.deleted - +b.deleted || + (communityType && +b.stickied - +a.stickied) || hotRankPost(b) - hotRankPost(a) ); } From 2c2918cc9e5e35c0b108b5daeeb2ccfbdee33e0c Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 4 Mar 2020 13:52:11 -0500 Subject: [PATCH 090/164] Moving comment voting to action bar. Adding plurals. --- ui/assets/css/main.css | 4 - ui/src/components/comment-node.tsx | 156 ++++++++++++++++------------- ui/src/components/post-listing.tsx | 7 +- ui/src/components/symbols.tsx | 8 +- ui/src/utils.ts | 6 +- ui/translations/en.json | 21 ++-- 6 files changed, 116 insertions(+), 86 deletions(-) diff --git a/ui/assets/css/main.css b/ui/assets/css/main.css index d206a508d..64f086b31 100644 --- a/ui/assets/css/main.css +++ b/ui/assets/css/main.css @@ -74,10 +74,6 @@ border-top: 2px solid var(--dark); } -.comment-node { - margin-bottom: 10px; -} - .vote-bar { margin-top: -6.5px; } diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index ecf5e2aaf..820dd9012 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -131,47 +131,13 @@ export class CommentNode extends Component { node.comment.parent_id && !this.props.noIndent ? 'ml-4' : '' }`} > - {!this.state.collapsed && ( -
            - -
            - {this.state.score} -
            - {WebSocketService.Instance.site.enable_downvotes && ( - - )} -
            - )}
            -
              +
              • { {i18n.t('banned')}
              • )} -
              • - - - - - {this.state.upvotes} - -
              • -
              • - - - - - {this.state.downvotes} - -
              • + +
              • + + + + + {this.state.score} + +
              • +
              • + + + + + {this.state.upvotes} + +
              • +
              • + + + + + {this.state.downvotes} + +
              • +
                {this.props.showCommunity && (
              • {i18n.t('to')} @@ -272,7 +253,7 @@ export class CommentNode extends Component { dangerouslySetInnerHTML={mdToHtml(this.commentUnlessRemoved)} /> )} -
                  +
                    {this.props.markable && (
                  • { )} {UserService.Instance.user && !this.props.viewOnly && ( <> +
                  • + +
                  • + {WebSocketService.Instance.site.enable_downvotes && ( +
                  • + +
                  • + )}
                  • {
                  • -
                  • - - - - - -
                  • {!this.myComment && (
                  • { title={i18n.t('link')} > - +
                  • @@ -360,6 +355,27 @@ export class CommentNode extends Component { ) : ( <> +
                  • + + + + + +
                  • { -
                    +
                    {this.state.score}
                    {WebSocketService.Instance.site.enable_downvotes && ( diff --git a/ui/src/components/symbols.tsx b/ui/src/components/symbols.tsx index 0c7169d53..b5d7dcdfe 100644 --- a/ui/src/components/symbols.tsx +++ b/ui/src/components/symbols.tsx @@ -15,6 +15,12 @@ export class Symbols extends Component { xmlnsXlink="http://www.w3.org/1999/xlink" > + + + + + + @@ -58,7 +64,7 @@ export class Symbols extends Component { - + diff --git a/ui/src/utils.ts b/ui/src/utils.ts index 5987070cf..46b393d9f 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -446,7 +446,7 @@ export function setupTribute(): Tribute { allowSpaces: false, autocompleteMode: true, menuItemLimit: mentionDropdownFetchLimit, - menuShowMinLength: 3, + menuShowMinLength: 2, }, // Users { @@ -460,7 +460,7 @@ export function setupTribute(): Tribute { allowSpaces: false, autocompleteMode: true, menuItemLimit: mentionDropdownFetchLimit, - menuShowMinLength: 3, + menuShowMinLength: 2, }, // Communities @@ -475,7 +475,7 @@ export function setupTribute(): Tribute { allowSpaces: false, autocompleteMode: true, menuItemLimit: mentionDropdownFetchLimit, - menuShowMinLength: 3, + menuShowMinLength: 2, }, ], }); diff --git a/ui/translations/en.json b/ui/translations/en.json index 1f959396a..b097b3f1b 100644 --- a/ui/translations/en.json +++ b/ui/translations/en.json @@ -4,14 +4,16 @@ "no_posts": "No Posts.", "create_a_post": "Create a post", "create_post": "Create Post", - "number_of_posts": "{{count}} Posts", + "number_of_posts": "{{count}} Post", + "number_of_posts_plural": "{{count}} Posts", "posts": "Posts", "related_posts": "These posts might be related", "cross_posts": "This link has also been posted to:", "cross_post": "cross-post", "cross_posted_to": "cross-posted to: ", "comments": "Comments", - "number_of_comments": "{{count}} Comments", + "number_of_comments": "{{count}} Comment", + "number_of_comments_plural": "{{count}} Comments", "remove_comment": "Remove Comment", "communities": "Communities", "users": "Users", @@ -21,7 +23,8 @@ "subscribed_to_communities": "Subscribed to <1>communities", "trending_communities": "Trending <1>communities", "list_of_communities": "List of communities", - "number_of_communities": "{{count}} Communities", + "number_of_communities": "{{count}} Community", + "number_of_communities_plural": "{{count}} Communities", "community_reqs": "lowercase, underscores, and no spaces.", "create_private_message": "Create Private Message", "send_secure_message": "Send Secure Message", @@ -79,10 +82,14 @@ "creator": "creator", "username": "Username", "email_or_username": "Email or Username", - "number_of_users": "{{count}} Users", - "number_of_subscribers": "{{count}} Subscribers", - "number_of_points": "{{count}} Points", - "number_online": "{{count}} Users Online", + "number_of_users": "{{count}} User", + "number_of_users_plural": "{{count}} Users", + "number_of_subscribers": "{{count}} Subscriber", + "number_of_subscribers_plural": "{{count}} Subscribers", + "number_of_points": "{{count}} Point", + "number_of_points_plural": "{{count}} Points", + "number_online": "{{count}} User Online", + "number_online_plural": "{{count}} Users Online", "name": "Name", "title": "Title", "category": "Category", From 7c274fdd35b30b645b08740987423bd633e3ba9e Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 4 Mar 2020 14:06:03 -0500 Subject: [PATCH 091/164] Add score color to bar --- ui/src/components/comment-node.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index 820dd9012..cfca0f3b1 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -181,7 +181,7 @@ export class CommentNode extends Component { })} >
                  • - + @@ -1144,4 +1144,14 @@ export class CommentNode extends Component { i.setState(i.state); setupTippy(); } + + get scoreColor() { + if (this.state.my_vote == 1) { + return 'text-info'; + } else if (this.state.my_vote == -1) { + return 'text-danger'; + } else { + return 'text-muted'; + } + } } From f601d6cd03a879c1f6e490cb4ecf954cb1d04d22 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 4 Mar 2020 14:13:08 -0500 Subject: [PATCH 092/164] Switching from heart to zap symbol for points. --- ui/src/components/comment-node.tsx | 2 +- ui/src/components/symbols.tsx | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index cfca0f3b1..9759a13a6 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -183,7 +183,7 @@ export class CommentNode extends Component {
                  • - + {this.state.score} diff --git a/ui/src/components/symbols.tsx b/ui/src/components/symbols.tsx index b5d7dcdfe..d44d0cfd1 100644 --- a/ui/src/components/symbols.tsx +++ b/ui/src/components/symbols.tsx @@ -15,6 +15,9 @@ export class Symbols extends Component { xmlnsXlink="http://www.w3.org/1999/xlink" > + + + From 43f783db42b3b48cacd330ac0b3abb561cef7c54 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 4 Mar 2020 14:18:13 -0500 Subject: [PATCH 093/164] Removing comment node other score colors for clarity. --- ui/src/components/comment-node.tsx | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index 9759a13a6..fba96db94 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -182,27 +182,23 @@ export class CommentNode extends Component { >
                  • - + {this.state.score}
                  • - - - - - {this.state.upvotes} - + + + + {this.state.upvotes}
                  • - - - - - {this.state.downvotes} - + + + + {this.state.downvotes}
                  • {this.props.showCommunity && ( From 6495cd2f65a279eb0124bcef600194e30de5590c Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 4 Mar 2020 14:24:45 -0500 Subject: [PATCH 094/164] Removing suffix on from now. --- ui/src/components/moment-time.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/src/components/moment-time.tsx b/ui/src/components/moment-time.tsx index 76e5fe289..4aba68d2b 100644 --- a/ui/src/components/moment-time.tsx +++ b/ui/src/components/moment-time.tsx @@ -36,7 +36,7 @@ export class MomentTime extends Component { - {moment.utc(this.props.data.updated).fromNow()} + {moment.utc(this.props.data.updated).fromNow(true)} ); } else { @@ -46,7 +46,7 @@ export class MomentTime extends Component { className="pointer unselectable" data-tippy-content={this.format(str)} > - {moment.utc(str).fromNow()} + {moment.utc(str).fromNow(true)} ); } From d14504763a90d9cc1bf91229ca0dde28a92a6bbc Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 4 Mar 2020 14:25:23 -0500 Subject: [PATCH 095/164] Adding separators for comment node title line. --- ui/src/components/comment-node.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index fba96db94..47930377c 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -174,6 +174,7 @@ export class CommentNode extends Component { {i18n.t('banned')} )} +
                  • { )} +
                  • From c999579c05ded42ea6ec68753d59b288123cc652 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 4 Mar 2020 22:35:55 -0500 Subject: [PATCH 096/164] Better tippy loading. Fixes #577 --- ui/src/components/comment-node.tsx | 12 ------------ ui/src/components/community.tsx | 2 ++ ui/src/components/main.tsx | 2 ++ ui/src/components/moment-time.tsx | 6 +----- ui/src/components/navbar.tsx | 7 +------ ui/src/components/post-listing.tsx | 13 ------------- ui/src/components/post.tsx | 5 +++++ ui/src/components/sidebar.tsx | 5 ----- ui/src/services/WebSocketService.ts | 15 +++++++++++---- 9 files changed, 22 insertions(+), 45 deletions(-) diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index 47930377c..a02a3cf85 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -103,18 +103,6 @@ export class CommentNode extends Component { this.handleCommentDownvote = this.handleCommentDownvote.bind(this); } - componentDidUpdate(prevProps: CommentNodeProps) { - let prevComment = prevProps.node.comment; - let comment = this.props.node.comment; - if ( - prevComment.saved !== comment.saved || - prevComment.deleted !== comment.deleted || - prevComment.read !== comment.read - ) { - setupTippy(); - } - } - componentWillReceiveProps(nextProps: CommentNodeProps) { this.state.my_vote = nextProps.node.comment.my_vote; this.state.upvotes = nextProps.node.comment.upvotes; diff --git a/ui/src/components/community.tsx b/ui/src/components/community.tsx index 0be549a5a..4e8e9d1b6 100644 --- a/ui/src/components/community.tsx +++ b/ui/src/components/community.tsx @@ -43,6 +43,7 @@ import { createPostLikeFindRes, editPostFindRes, commentsToFlatNodes, + setupTippy, } from '../utils'; import { i18n } from '../i18next'; @@ -341,6 +342,7 @@ export class Community extends Component { this.state.posts = data.posts; this.state.loading = false; this.setState(this.state); + setupTippy(); } else if (res.op == UserOperation.EditPost) { let data = res.data as PostResponse; editPostFindRes(data, this.state.posts); diff --git a/ui/src/components/main.tsx b/ui/src/components/main.tsx index 85fa5d339..ee028151f 100644 --- a/ui/src/components/main.tsx +++ b/ui/src/components/main.tsx @@ -52,6 +52,7 @@ import { editPostFindRes, commentsToFlatNodes, commentSortSortType, + setupTippy, } from '../utils'; import { i18n } from '../i18next'; import { T } from 'inferno-i18next'; @@ -620,6 +621,7 @@ export class Main extends Component { this.state.posts = data.posts; this.state.loading = false; this.setState(this.state); + setupTippy(); } else if (res.op == UserOperation.CreatePost) { let data = res.data as PostResponse; diff --git a/ui/src/components/moment-time.tsx b/ui/src/components/moment-time.tsx index 4aba68d2b..24ab2d897 100644 --- a/ui/src/components/moment-time.tsx +++ b/ui/src/components/moment-time.tsx @@ -1,6 +1,6 @@ import { Component } from 'inferno'; import moment from 'moment'; -import { getMomentLanguage, setupTippy, capitalizeFirstLetter } from '../utils'; +import { getMomentLanguage, capitalizeFirstLetter } from '../utils'; import { i18n } from '../i18next'; interface MomentTimeProps { @@ -20,10 +20,6 @@ export class MomentTime extends Component { moment.locale(lang); } - componentDidMount() { - setupTippy(); - } - render() { if (this.props.data.updated) { return ( diff --git a/ui/src/components/navbar.tsx b/ui/src/components/navbar.tsx index 031c2ecbf..ef3f84309 100644 --- a/ui/src/components/navbar.tsx +++ b/ui/src/components/navbar.tsx @@ -26,7 +26,6 @@ import { fetchLimit, isCommentType, toast, - setupTippy, } from '../utils'; import { version } from '../version'; import { i18n } from '../i18next'; @@ -85,10 +84,6 @@ export class Navbar extends Component { WebSocketService.Instance.getSite(); } - componentDidMount() { - setupTippy(); - } - render() { return this.navbar(); } @@ -283,7 +278,7 @@ export class Navbar extends Component { } else if (res.op == UserOperation.GetSite) { let data = res.data as GetSiteResponse; - if (data.site) { + if (data.site && !this.state.siteName) { this.state.siteName = data.site.name; WebSocketService.Instance.site = data.site; this.setState(this.state); diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index e170c711a..f8f194486 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -102,19 +102,6 @@ export class PostListing extends Component { } } - componentDidUpdate(prevProps: PostListingProps) { - let prevPost = prevProps.post; - let post = this.props.post; - if ( - prevPost.saved !== post.saved || - prevPost.deleted !== post.deleted || - prevPost.locked !== post.locked || - prevPost.stickied !== post.stickied - ) { - setupTippy(); - } - } - componentWillReceiveProps(nextProps: PostListingProps) { this.state.my_vote = nextProps.post.my_vote; this.state.upvotes = nextProps.post.upvotes; diff --git a/ui/src/components/post.tsx b/ui/src/components/post.tsx index d8f662cf7..faee23ed0 100644 --- a/ui/src/components/post.tsx +++ b/ui/src/components/post.tsx @@ -37,6 +37,7 @@ import { createCommentLikeRes, createPostLikeRes, commentsToFlatNodes, + setupTippy, } from '../utils'; import { PostListing } from './post-listing'; import { PostListings } from './post-listings'; @@ -370,6 +371,7 @@ export class Post extends Component { } this.setState(this.state); + setupTippy(); } else if (res.op == UserOperation.CreateComment) { let data = res.data as CommentResponse; @@ -386,6 +388,7 @@ export class Post extends Component { let data = res.data as CommentResponse; saveCommentRes(data, this.state.comments); this.setState(this.state); + setupTippy(); } else if (res.op == UserOperation.CreateCommentLike) { let data = res.data as CommentResponse; createCommentLikeRes(data, this.state.comments); @@ -398,10 +401,12 @@ export class Post extends Component { let data = res.data as PostResponse; this.state.post = data.post; this.setState(this.state); + setupTippy(); } else if (res.op == UserOperation.SavePost) { let data = res.data as PostResponse; this.state.post = data.post; this.setState(this.state); + setupTippy(); } else if (res.op == UserOperation.EditCommunity) { let data = res.data as CommunityResponse; this.state.community = data.community; diff --git a/ui/src/components/sidebar.tsx b/ui/src/components/sidebar.tsx index a78589d55..042b67dbb 100644 --- a/ui/src/components/sidebar.tsx +++ b/ui/src/components/sidebar.tsx @@ -13,7 +13,6 @@ import { getUnixTime, pictshareAvatarThumbnail, showAvatars, - setupTippy, } from '../utils'; import { CommunityForm } from './community-form'; import { i18n } from '../i18next'; @@ -47,10 +46,6 @@ export class Sidebar extends Component { this.handleEditCancel = this.handleEditCancel.bind(this); } - componentDidUpdate() { - setupTippy(); - } - render() { return (
                    diff --git a/ui/src/services/WebSocketService.ts b/ui/src/services/WebSocketService.ts index 3df69457a..02c97cc94 100644 --- a/ui/src/services/WebSocketService.ts +++ b/ui/src/services/WebSocketService.ts @@ -61,6 +61,7 @@ export class WebSocketService { private constructor() { this.ws = new ReconnectingWebSocket(wsUri); + let firstConnect = true; this.subject = Observable.create((obs: any) => { this.ws.onmessage = e => { @@ -68,13 +69,19 @@ export class WebSocketService { }; this.ws.onopen = () => { console.log(`Connected to ${wsUri}`); + if (UserService.Instance.user) { this.userJoin(); } - let res: WebSocketJsonResponse = { - reconnect: true, - }; - obs.next(res); + + if (!firstConnect) { + let res: WebSocketJsonResponse = { + reconnect: true, + }; + obs.next(res); + } + + firstConnect = false; }; }).pipe(share()); } From eeef752a5ce1af441c69a701d95ba8a2dec651c4 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 4 Mar 2020 23:36:42 -0500 Subject: [PATCH 097/164] Adding left border color, removing indent. --- ui/src/components/comment-node.tsx | 881 +++++++++++++++-------------- ui/src/utils.ts | 4 + 2 files changed, 454 insertions(+), 431 deletions(-) diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index a02a3cf85..0e62e6eb5 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -27,6 +27,7 @@ import { pictshareAvatarThumbnail, showAvatars, setupTippy, + randomHsl, } from '../utils'; import moment from 'moment'; import { MomentTime } from './moment-time'; @@ -54,6 +55,7 @@ interface CommentNodeState { score: number; upvotes: number; downvotes: number; + borderColor: string; } interface CommentNodeProps { @@ -92,6 +94,7 @@ export class CommentNode extends Component { score: this.props.node.comment.score, upvotes: this.props.node.comment.upvotes, downvotes: this.props.node.comment.downvotes, + borderColor: randomHsl(), }; constructor(props: any, context: any) { @@ -116,7 +119,7 @@ export class CommentNode extends Component { return (
                    { className={`details comment-node mb-1 ${ this.isCommentNew ? 'mark' : '' }`} + style={ + !this.props.noIndent && + `border-left: 1px solid; border-color: ${this.state.borderColor} !important` + } > -
                      -
                    • - - {node.comment.creator_avatar && showAvatars() && ( - - )} - {node.comment.creator_name} - -
                    • - {this.isMod && ( -
                    • - {i18n.t('mod')} -
                    • - )} - {this.isAdmin && ( -
                    • - {i18n.t('admin')} -
                    • - )} - {this.isPostCreator && ( -
                    • - {i18n.t('creator')} -
                    • - )} - {(node.comment.banned_from_community || node.comment.banned) && ( -
                    • - {i18n.t('banned')} -
                    • - )} -
                    • - +
                      +
                      • - - - + + {node.comment.creator_avatar && showAvatars() && ( + + )} + {node.comment.creator_name} + +
                      • + {this.isMod && ( +
                      • + {i18n.t('mod')} +
                      • + )} + {this.isAdmin && ( +
                      • + {i18n.t('admin')} +
                      • + )} + {this.isPostCreator && ( +
                      • + {i18n.t('creator')} +
                      • + )} + {(node.comment.banned_from_community || node.comment.banned) && ( +
                      • + {i18n.t('banned')} +
                      • + )} +
                      • + +
                      • + + + + + {this.state.score} + +
                      • +
                      • + + - {this.state.score} + {this.state.upvotes} +
                      • +
                      • + + + + {this.state.downvotes} +
                      • +
                        + {this.props.showCommunity && ( +
                      • + {i18n.t('to')} + + {node.comment.community_name} + +
                      • + )} +
                      • +
                      • + +
                      • - - - - {this.state.upvotes} -
                      • -
                      • - - - - {this.state.downvotes} -
                      • - - {this.props.showCommunity && ( -
                      • - {i18n.t('to')} - - {node.comment.community_name} - -
                      • - )} -
                      • -
                      • - - - -
                      • -
                      • -
                        - {this.state.collapsed ? ( - - - - ) : ( - - - - )} -
                        -
                      • -
                      - {this.state.showEdit && ( - - )} - {!this.state.showEdit && !this.state.collapsed && ( -
                      - {this.state.viewSource ? ( -
                      {this.commentUnlessRemoved}
                      - ) : (
                      - )} -
                        - {this.props.markable && ( -
                      • - - - - - -
                      • - )} - {UserService.Instance.user && !this.props.viewOnly && ( - <> -
                      • - -
                      • - {WebSocketService.Instance.site.enable_downvotes && ( -
                      • - -
                      • + className="unselectable pointer text-monospace" + onClick={linkEvent(this, this.handleCommentCollapse)} + > + {this.state.collapsed ? ( + + + + ) : ( + + + + )} +
                      + +
                    + {this.state.showEdit && ( + + )} + {!this.state.showEdit && !this.state.collapsed && ( +
                    + {this.state.viewSource ? ( +
                    {this.commentUnlessRemoved}
                    + ) : ( +
                    + )} +
                      + {this.props.markable && (
                    • - - + +
                    • - {!this.myComment && ( + )} + {UserService.Instance.user && !this.props.viewOnly && ( + <>
                    • - - + - +
                    • - )} -
                    • - - - - - -
                    • - {!this.state.showAdvanced ? ( + {WebSocketService.Instance.site.enable_downvotes && ( +
                    • + +
                    • + )}
                    • - +
                    • - ) : ( - <> + {!this.myComment && ( +
                    • + + + + + +
                    • + )} +
                    • + + + + + +
                    • + {!this.state.showAdvanced ? (
                    • - - + +
                    • -
                    • - - - - - -
                    • - {this.myComment && ( - <> -
                    • -
                    • - - - - - -
                    • -
                    • - - - - - -
                    • - - )} - {/* Admins and mods can remove comments */} - {(this.canMod || this.canAdmin) && ( - <> -
                    • - {!node.comment.removed ? ( - - {i18n.t('remove')} - - ) : ( - - {i18n.t('restore')} - + ) : ( + <> +
                    • + - - )} - {/* Mods can ban from community, and appoint as mods to community */} - {this.canMod && ( - <> - {!this.isMod && ( + data-tippy-content={ + node.comment.saved + ? i18n.t('unsave') + : i18n.t('save') + } + > + + + + +
                    • +
                    • + + + + + +
                    • + {this.myComment && ( + <> +
                    • - {!node.comment.banned_from_community ? ( + + + + + +
                    • +
                    • + + + + + +
                    • + + )} + {/* Admins and mods can remove comments */} + {(this.canMod || this.canAdmin) && ( + <> +
                    • + {!node.comment.removed ? ( - {i18n.t('ban')} + {i18n.t('remove')} ) : ( - {i18n.t('unban')} + {i18n.t('restore')} )}
                    • - )} - {!node.comment.banned_from_community && ( + + )} + {/* Mods can ban from community, and appoint as mods to community */} + {this.canMod && ( + <> + {!this.isMod && ( +
                    • + {!node.comment.banned_from_community ? ( + + {i18n.t('ban')} + + ) : ( + + {i18n.t('unban')} + + )} +
                    • + )} + {!node.comment.banned_from_community && ( +
                    • + {!this.state.showConfirmAppointAsMod ? ( + + {this.isMod + ? i18n.t('remove_as_mod') + : i18n.t('appoint_as_mod')} + + ) : ( + <> + + {i18n.t('are_you_sure')} + + + {i18n.t('yes')} + + + {i18n.t('no')} + + + )} +
                    • + )} + + )} + {/* Community creators and admins can transfer community to another mod */} + {(this.amCommunityCreator || this.canAdmin) && + this.isMod && (
                    • - {!this.state.showConfirmAppointAsMod ? ( + {!this.state.showConfirmTransferCommunity ? ( - {this.isMod - ? i18n.t('remove_as_mod') - : i18n.t('appoint_as_mod')} + {i18n.t('transfer_community')} ) : ( <> @@ -492,7 +553,7 @@ export class CommentNode extends Component { class="pointer d-inline-block mr-1" onClick={linkEvent( this, - this.handleAddModToCommunity + this.handleTransferCommunity )} > {i18n.t('yes')} @@ -501,7 +562,8 @@ export class CommentNode extends Component { class="pointer d-inline-block" onClick={linkEvent( this, - this.handleCancelConfirmAppointAsMod + this + .handleCancelShowConfirmTransferCommunity )} > {i18n.t('no')} @@ -510,21 +572,89 @@ export class CommentNode extends Component { )}
                    • )} - - )} - {/* Community creators and admins can transfer community to another mod */} - {(this.amCommunityCreator || this.canAdmin) && - this.isMod && ( + {/* Admins can ban from all, and appoint other admins */} + {this.canAdmin && ( + <> + {!this.isAdmin && ( +
                    • + {!node.comment.banned ? ( + + {i18n.t('ban_from_site')} + + ) : ( + + {i18n.t('unban_from_site')} + + )} +
                    • + )} + {!node.comment.banned && ( +
                    • + {!this.state.showConfirmAppointAsAdmin ? ( + + {this.isAdmin + ? i18n.t('remove_as_admin') + : i18n.t('appoint_as_admin')} + + ) : ( + <> + + {i18n.t('are_you_sure')} + + + {i18n.t('yes')} + + + {i18n.t('no')} + + + )} +
                    • + )} + + )} + {/* Site Creator can transfer to another admin */} + {this.amSiteCreator && this.isAdmin && (
                    • - {!this.state.showConfirmTransferCommunity ? ( + {!this.state.showConfirmTransferSite ? ( - {i18n.t('transfer_community')} + {i18n.t('transfer_site')} ) : ( <> @@ -535,7 +665,7 @@ export class CommentNode extends Component { class="pointer d-inline-block mr-1" onClick={linkEvent( this, - this.handleTransferCommunity + this.handleTransferSite )} > {i18n.t('yes')} @@ -544,8 +674,7 @@ export class CommentNode extends Component { class="pointer d-inline-block" onClick={linkEvent( this, - this - .handleCancelShowConfirmTransferCommunity + this.handleCancelShowConfirmTransferSite )} > {i18n.t('no')} @@ -554,124 +683,14 @@ export class CommentNode extends Component { )}
                    • )} - {/* Admins can ban from all, and appoint other admins */} - {this.canAdmin && ( - <> - {!this.isAdmin && ( -
                    • - {!node.comment.banned ? ( - - {i18n.t('ban_from_site')} - - ) : ( - - {i18n.t('unban_from_site')} - - )} -
                    • - )} - {!node.comment.banned && ( -
                    • - {!this.state.showConfirmAppointAsAdmin ? ( - - {this.isAdmin - ? i18n.t('remove_as_admin') - : i18n.t('appoint_as_admin')} - - ) : ( - <> - - {i18n.t('are_you_sure')} - - - {i18n.t('yes')} - - - {i18n.t('no')} - - - )} -
                    • - )} - - )} - {/* Site Creator can transfer to another admin */} - {this.amSiteCreator && this.isAdmin && ( -
                    • - {!this.state.showConfirmTransferSite ? ( - - {i18n.t('transfer_site')} - - ) : ( - <> - - {i18n.t('are_you_sure')} - - - {i18n.t('yes')} - - - {i18n.t('no')} - - - )} -
                    • - )} - - )} - - )} -
                    -
                    - )} + + )} + + )} +
                  +
            + )} +
            {this.state.showRemoveDialog && ( Date: Thu, 5 Mar 2020 00:02:08 -0500 Subject: [PATCH 098/164] Changing user names to bold text-body. Removing color lines on first comment. --- ui/src/components/comment-node.tsx | 9 +++++++-- ui/src/components/main.tsx | 9 ++++++--- ui/src/components/modlog.tsx | 2 +- ui/src/components/post-listing.tsx | 5 ++++- ui/src/components/sidebar.tsx | 5 ++++- 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index 0e62e6eb5..9b476c7eb 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -129,14 +129,19 @@ export class CommentNode extends Component { }`} style={ !this.props.noIndent && + this.props.node.comment.parent_id && `border-left: 1px solid; border-color: ${this.state.borderColor} !important` } > -
            +
            • {node.comment.creator_avatar && showAvatars() && ( diff --git a/ui/src/components/main.tsx b/ui/src/components/main.tsx index ee028151f..b772bd872 100644 --- a/ui/src/components/main.tsx +++ b/ui/src/components/main.tsx @@ -184,7 +184,7 @@ export class Main extends Component {
              # - + # @@ -222,7 +222,7 @@ export class Main extends Component {
              # - + # @@ -317,7 +317,10 @@ export class Main extends Component {
            • {i18n.t('admins')}:
            • {this.state.siteRes.admins.map(admin => (
            • - + {admin.avatar && showAvatars() && ( {
              {this.state.communityName && ( /c/{this.state.communityName}{' '} diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index f8f194486..0441bab84 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -425,7 +425,10 @@ export class PostListing extends Component {
              • {i18n.t('by')} - + {post.creator_avatar && showAvatars() && ( {
              • {i18n.t('mods')}:
              • {this.props.moderators.map(mod => (
              • - + {mod.avatar && showAvatars() && ( Date: Thu, 5 Mar 2020 00:39:22 -0500 Subject: [PATCH 099/164] Change post-listing vote colors. --- ui/src/components/post-listing.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index 0441bab84..bfa58a739 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -469,7 +469,7 @@ export class PostListing extends Component {
              • - + @@ -477,7 +477,7 @@ export class PostListing extends Component {
              • - + From 91ae9a9d490a26303a7b74e4ac3263a5acc5c81a Mon Sep 17 00:00:00 2001 From: Felix Date: Thu, 5 Mar 2020 11:32:29 +0100 Subject: [PATCH 100/164] Revert "pull in activitypub library" This reverts commit a52a954eb4b79237941fc21b6989856d4e3d44a3. --- README.md | 6 - server/Cargo.lock | 5 - server/Cargo.toml | 10 +- server/src/activitypub/activity.rs | 1496 ------------------- server/src/activitypub/actor/mod.rs | 291 ---- server/src/activitypub/actor/properties.rs | 129 -- server/src/activitypub/collection.rs | 264 ---- server/src/activitypub/endpoint.rs | 107 -- server/src/activitypub/link.rs | 23 - server/src/activitypub/mod.rs | 57 - server/src/activitypub/object/mod.rs | 444 ------ server/src/activitypub/object/properties.rs | 111 -- server/src/apub/community.rs | 2 +- server/src/apub/post.rs | 2 +- server/src/apub/puller.rs | 2 +- server/src/apub/user.rs | 2 +- server/src/lib.rs | 1 - 17 files changed, 5 insertions(+), 2947 deletions(-) delete mode 100644 server/src/activitypub/activity.rs delete mode 100644 server/src/activitypub/actor/mod.rs delete mode 100644 server/src/activitypub/actor/properties.rs delete mode 100644 server/src/activitypub/collection.rs delete mode 100644 server/src/activitypub/endpoint.rs delete mode 100644 server/src/activitypub/link.rs delete mode 100644 server/src/activitypub/mod.rs delete mode 100644 server/src/activitypub/object/mod.rs delete mode 100644 server/src/activitypub/object/properties.rs diff --git a/README.md b/README.md index fb99d301a..81eceb74a 100644 --- a/README.md +++ b/README.md @@ -165,9 +165,3 @@ ts-node translation_report.ts ## Credits Logo made by Andy Cuccaro (@andycuccaro) under the CC-BY-SA 4.0 license. - -## License - -All code is licensed under AGPLv3 unless otherwise indicated. - -The code in `server/src/activitypub` is taken from the [Aardwolf/activitypub](https://crates.io/crates/activitypub) crate and licensed under GPLv3. diff --git a/server/Cargo.lock b/server/Cargo.lock index 242fa7893..125319206 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -1493,15 +1493,11 @@ name = "lemmy_server" version = "0.0.1" dependencies = [ "activitypub 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "activitystreams-derive 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "activitystreams-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "activitystreams-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "actix 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "actix-files 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "actix-rt 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "actix-web 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "actix-web-actors 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", "bcrypt 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "config 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1521,7 +1517,6 @@ dependencies = [ "reqwest 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)", "rss 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "strum 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/server/Cargo.toml b/server/Cargo.toml index 1b6ea3053..828230572 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -13,7 +13,6 @@ activitypub = "0.2.0" chrono = { version = "0.4.7", features = ["serde"] } failure = "0.1.5" serde_json = { version = "1.0.45", features = ["preserve_order"]} -serde_derive = "1.0" serde = { version = "1.0.94", features = ["derive"] } actix = "0.9.0" actix-web = "2.0.0" @@ -34,11 +33,4 @@ rss = "1.9.0" htmlescape = "0.3.1" config = "0.10.1" hjson = "0.8.2" -reqwest = "0.9.24" -activitystreams-derive = "0.2" -activitystreams-traits = "0.2" -activitystreams-types = "0.3" - - -[dev-dependencies] -anyhow = "1.0" +reqwest = "0.9.24" \ No newline at end of file diff --git a/server/src/activitypub/activity.rs b/server/src/activitypub/activity.rs deleted file mode 100644 index c92b6eaf7..000000000 --- a/server/src/activitypub/activity.rs +++ /dev/null @@ -1,1496 +0,0 @@ -/* - * This file is part of ActivityPub. - * - * Copyright © 2018 Riley Trautman - * - * ActivityPub is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ActivityPub is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ActivityPub. If not, see . - */ - -//! Activity traits and types - -pub use activitystreams_traits::{Activity, IntransitiveActivity}; -pub use activitystreams_types::activity::{kind, properties, ActivityExt}; -use serde_derive::{Deserialize, Serialize}; - -use self::{kind::*, properties::*}; -use activitypub::object::{ - properties::{ApObjectProperties, ObjectProperties}, - ApObjectExt, Object, ObjectExt, -}; - -/// Indicates that the actor accepts the object. -/// -/// The target property can be used in certain circumstances to indicate the context into which the -/// object has been accepted. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Accept { - #[serde(rename = "type")] - kind: AcceptType, - - #[serde(flatten)] - pub accept_props: AcceptProperties, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - #[serde(flatten)] - pub activity_props: ActivityProperties, -} - -impl Object for Accept {} -impl ObjectExt for Accept { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Accept { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Activity for Accept {} -impl ActivityExt for Accept { - fn props(&self) -> &ActivityProperties { - &self.activity_props - } - - fn props_mut(&mut self) -> &mut ActivityProperties { - &mut self.activity_props - } -} - -/// Indicates that the actor has added the object to the target. -/// -/// If the target property is not explicitly specified, the target would need to be determined -/// implicitly by context. The origin can be used to identify the context from which the object -/// originated. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Add { - #[serde(rename = "type")] - kind: AddType, - - #[serde(flatten)] - pub add_props: AddProperties, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - #[serde(flatten)] - pub activity_props: ActivityProperties, -} - -impl Object for Add {} -impl ObjectExt for Add { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Add { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Activity for Add {} -impl ActivityExt for Add { - fn props(&self) -> &ActivityProperties { - &self.activity_props - } - - fn props_mut(&mut self) -> &mut ActivityProperties { - &mut self.activity_props - } -} - -/// Indicates that the actor has moved object from origin to target. -/// -/// If the origin or target are not specified, either can be determined by context. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct AMove { - #[serde(rename = "type")] - kind: MoveType, - - #[serde(flatten)] - pub move_props: MoveProperties, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - #[serde(flatten)] - pub activity_props: ActivityProperties, -} - -impl Object for AMove {} -impl ObjectExt for AMove { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for AMove { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Activity for AMove {} -impl ActivityExt for AMove { - fn props(&self) -> &ActivityProperties { - &self.activity_props - } - - fn props_mut(&mut self) -> &mut ActivityProperties { - &mut self.activity_props - } -} - -/// Indicates that the actor is calling the target's attention the object. -/// -/// The origin typically has no defined meaning. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Announce { - #[serde(rename = "type")] - kind: AnnounceType, - - #[serde(flatten)] - pub announce_props: AnnounceProperties, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - #[serde(flatten)] - pub activity_props: ActivityProperties, -} - -impl Object for Announce {} -impl ObjectExt for Announce { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Announce { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Activity for Announce {} -impl ActivityExt for Announce { - fn props(&self) -> &ActivityProperties { - &self.activity_props - } - - fn props_mut(&mut self) -> &mut ActivityProperties { - &mut self.activity_props - } -} - -/// An IntransitiveActivity that indicates that the actor has arrived at the location. -/// -/// The origin can be used to identify the context from which the actor originated. The target -/// typically has no defined meaning. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Arrive { - #[serde(rename = "type")] - kind: ArriveType, - - #[serde(flatten)] - pub arrive_props: ArriveProperties, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - #[serde(flatten)] - pub activity_props: ActivityProperties, -} - -impl Object for Arrive {} -impl ObjectExt for Arrive { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Arrive { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Activity for Arrive {} -impl ActivityExt for Arrive { - fn props(&self) -> &ActivityProperties { - &self.activity_props - } - - fn props_mut(&mut self) -> &mut ActivityProperties { - &mut self.activity_props - } -} -impl IntransitiveActivity for Arrive {} - -/// Indicates that the actor is blocking the object. -/// -/// Blocking is a stronger form of Ignore. The typical use is to support social systems that allow -/// one user to block activities or content of other users. The target and origin typically have no -/// defined meaning. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Block { - #[serde(rename = "type")] - kind: BlockType, - - #[serde(flatten)] - pub block_props: BlockProperties, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - #[serde(flatten)] - pub activity_props: ActivityProperties, -} - -impl Object for Block {} -impl ObjectExt for Block { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Block { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Activity for Block {} -impl ActivityExt for Block { - fn props(&self) -> &ActivityProperties { - &self.activity_props - } - - fn props_mut(&mut self) -> &mut ActivityProperties { - &mut self.activity_props - } -} - -/// Indicates that the actor has created the object. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Create { - #[serde(rename = "type")] - kind: CreateType, - - #[serde(flatten)] - pub create_props: CreateProperties, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - #[serde(flatten)] - pub activity_props: ActivityProperties, -} - -impl Object for Create {} -impl ObjectExt for Create { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Create { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Activity for Create {} -impl ActivityExt for Create { - fn props(&self) -> &ActivityProperties { - &self.activity_props - } - - fn props_mut(&mut self) -> &mut ActivityProperties { - &mut self.activity_props - } -} - -/// Indicates that the actor has deleted the object. -/// -/// If specified, the origin indicates the context from which the object was deleted. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Delete { - #[serde(rename = "type")] - kind: DeleteType, - - #[serde(flatten)] - pub delete_props: DeleteProperties, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - #[serde(flatten)] - pub activity_props: ActivityProperties, -} - -impl Object for Delete {} -impl ObjectExt for Delete { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Delete { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Activity for Delete {} -impl ActivityExt for Delete { - fn props(&self) -> &ActivityProperties { - &self.activity_props - } - - fn props_mut(&mut self) -> &mut ActivityProperties { - &mut self.activity_props - } -} - -/// Indicates that the actor dislikes the object. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Dislike { - #[serde(rename = "type")] - kind: DislikeType, - - #[serde(flatten)] - pub dislike_props: DislikeProperties, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - #[serde(flatten)] - pub activity_props: ActivityProperties, -} - -impl Object for Dislike {} -impl ObjectExt for Dislike { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Dislike { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Activity for Dislike {} -impl ActivityExt for Dislike { - fn props(&self) -> &ActivityProperties { - &self.activity_props - } - - fn props_mut(&mut self) -> &mut ActivityProperties { - &mut self.activity_props - } -} - -/// Indicates that the actor is "flagging" the object. -/// -/// Flagging is defined in the sense common to many social platforms as reporting content as being -/// inappropriate for any number of reasons. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Flag { - #[serde(rename = "type")] - kind: FlagType, - - #[serde(flatten)] - pub flag_props: FlagProperties, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - #[serde(flatten)] - pub activity_props: ActivityProperties, -} - -impl Object for Flag {} -impl ObjectExt for Flag { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Flag { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Activity for Flag {} -impl ActivityExt for Flag { - fn props(&self) -> &ActivityProperties { - &self.activity_props - } - - fn props_mut(&mut self) -> &mut ActivityProperties { - &mut self.activity_props - } -} - -/// Indicates that the actor is "following" the object. -/// -/// Following is defined in the sense typically used within Social systems in which the actor is -/// interested in any activity performed by or on the object. The target and origin typically have -/// no defined meaning. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Follow { - #[serde(rename = "type")] - kind: FollowType, - - #[serde(flatten)] - pub follow_props: FollowProperties, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - #[serde(flatten)] - pub activity_props: ActivityProperties, -} - -impl Object for Follow {} -impl ObjectExt for Follow { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Follow { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Activity for Follow {} -impl ActivityExt for Follow { - fn props(&self) -> &ActivityProperties { - &self.activity_props - } - - fn props_mut(&mut self) -> &mut ActivityProperties { - &mut self.activity_props - } -} - -/// Indicates that the actor is ignoring the object. -/// -/// The target and origin typically have no defined meaning. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Ignore { - #[serde(rename = "type")] - kind: IgnoreType, - - #[serde(flatten)] - pub ignore_props: IgnoreProperties, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - #[serde(flatten)] - pub activity_props: ActivityProperties, -} - -impl Object for Ignore {} -impl ObjectExt for Ignore { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Ignore { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Activity for Ignore {} -impl ActivityExt for Ignore { - fn props(&self) -> &ActivityProperties { - &self.activity_props - } - - fn props_mut(&mut self) -> &mut ActivityProperties { - &mut self.activity_props - } -} - -/// A specialization of Offer in which the actor is extending an invitation for the object to the -/// target. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Invite { - #[serde(rename = "type")] - kind: InviteType, - - #[serde(flatten)] - pub invite_props: InviteProperties, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - #[serde(flatten)] - pub activity_props: ActivityProperties, -} - -impl Object for Invite {} -impl ObjectExt for Invite { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Invite { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Activity for Invite {} -impl ActivityExt for Invite { - fn props(&self) -> &ActivityProperties { - &self.activity_props - } - - fn props_mut(&mut self) -> &mut ActivityProperties { - &mut self.activity_props - } -} - -/// Indicates that the actor has joined the object. -/// -/// The target and origin typically have no defined meaning -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Join { - #[serde(rename = "type")] - kind: JoinType, - - #[serde(flatten)] - pub join_props: JoinProperties, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - #[serde(flatten)] - pub activity_props: ActivityProperties, -} - -impl Object for Join {} -impl ObjectExt for Join { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Join { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Activity for Join {} -impl ActivityExt for Join { - fn props(&self) -> &ActivityProperties { - &self.activity_props - } - - fn props_mut(&mut self) -> &mut ActivityProperties { - &mut self.activity_props - } -} - -/// Indicates that the actor has left the object. -/// -/// The target and origin typically have no meaning. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Leave { - #[serde(rename = "type")] - kind: LeaveType, - - #[serde(flatten)] - pub leave_props: LeaveProperties, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - #[serde(flatten)] - pub activity_props: ActivityProperties, -} - -impl Object for Leave {} -impl ObjectExt for Leave { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Leave { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Activity for Leave {} -impl ActivityExt for Leave { - fn props(&self) -> &ActivityProperties { - &self.activity_props - } - - fn props_mut(&mut self) -> &mut ActivityProperties { - &mut self.activity_props - } -} - -/// Indicates that the actor likes, recommends or endorses the object. -/// -/// The target and origin typically have no defined meaning. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Like { - #[serde(rename = "type")] - kind: LikeType, - - #[serde(flatten)] - pub like_props: LikeProperties, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - #[serde(flatten)] - pub activity_props: ActivityProperties, -} - -impl Object for Like {} -impl ObjectExt for Like { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Like { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Activity for Like {} -impl ActivityExt for Like { - fn props(&self) -> &ActivityProperties { - &self.activity_props - } - - fn props_mut(&mut self) -> &mut ActivityProperties { - &mut self.activity_props - } -} - -/// Indicates that the actor has listened to the object. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Listen { - #[serde(rename = "type")] - kind: ListenType, - - #[serde(flatten)] - pub listen_props: ListenProperties, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - #[serde(flatten)] - pub activity_props: ActivityProperties, -} - -impl Object for Listen {} -impl ObjectExt for Listen { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Listen { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Activity for Listen {} -impl ActivityExt for Listen { - fn props(&self) -> &ActivityProperties { - &self.activity_props - } - - fn props_mut(&mut self) -> &mut ActivityProperties { - &mut self.activity_props - } -} - -/// Indicates that the actor is offering the object. -/// -/// If specified, the target indicates the entity to which the object is being offered. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Offer { - #[serde(rename = "type")] - kind: OfferType, - - #[serde(flatten)] - pub offer_props: OfferProperties, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - #[serde(flatten)] - pub activity_props: ActivityProperties, -} - -impl Object for Offer {} -impl ObjectExt for Offer { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Offer { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Activity for Offer {} -impl ActivityExt for Offer { - fn props(&self) -> &ActivityProperties { - &self.activity_props - } - - fn props_mut(&mut self) -> &mut ActivityProperties { - &mut self.activity_props - } -} - -/// Represents a question being asked. -/// -/// Question objects are an extension of IntransitiveActivity. That is, the Question object is an -/// Activity, but the direct object is the question itself and therefore it would not contain an -/// object property. -/// -/// Either of the anyOf and oneOf properties MAY be used to express possible answers, but a -/// Question object MUST NOT have both properties. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Question { - #[serde(rename = "type")] - kind: QuestionType, - - #[serde(flatten)] - pub question_props: QuestionProperties, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - #[serde(flatten)] - pub activity_props: ActivityProperties, -} - -impl Object for Question {} -impl ObjectExt for Question { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Question { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Activity for Question {} -impl ActivityExt for Question { - fn props(&self) -> &ActivityProperties { - &self.activity_props - } - - fn props_mut(&mut self) -> &mut ActivityProperties { - &mut self.activity_props - } -} -impl IntransitiveActivity for Question {} - -/// Indicates that the actor has read the object. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Read { - #[serde(rename = "type")] - kind: ReadType, - - #[serde(flatten)] - pub read_props: ReadProperties, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - #[serde(flatten)] - pub activity_props: ActivityProperties, -} - -impl Object for Read {} -impl ObjectExt for Read { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Read { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Activity for Read {} -impl ActivityExt for Read { - fn props(&self) -> &ActivityProperties { - &self.activity_props - } - - fn props_mut(&mut self) -> &mut ActivityProperties { - &mut self.activity_props - } -} - -/// Indicates that the actor is rejecting the object. -/// -/// The target and origin typically have no defined meaning. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Reject { - #[serde(rename = "type")] - kind: RejectType, - - #[serde(flatten)] - pub reject_props: RejectProperties, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - #[serde(flatten)] - pub activity_props: ActivityProperties, -} - -impl Object for Reject {} -impl ObjectExt for Reject { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Reject { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Activity for Reject {} -impl ActivityExt for Reject { - fn props(&self) -> &ActivityProperties { - &self.activity_props - } - - fn props_mut(&mut self) -> &mut ActivityProperties { - &mut self.activity_props - } -} - -/// Indicates that the actor is removing the object. -/// -/// If specified, the origin indicates the context from which the object is being removed. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Remove { - #[serde(rename = "type")] - kind: RemoveType, - - #[serde(flatten)] - pub remove_props: RemoveProperties, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - #[serde(flatten)] - pub activity_props: ActivityProperties, -} - -impl Object for Remove {} -impl ObjectExt for Remove { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Remove { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Activity for Remove {} -impl ActivityExt for Remove { - fn props(&self) -> &ActivityProperties { - &self.activity_props - } - - fn props_mut(&mut self) -> &mut ActivityProperties { - &mut self.activity_props - } -} - -/// A specialization of Accept indicating that the acceptance is tentative. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct TentativeAccept { - #[serde(rename = "type")] - kind: TentativeAcceptType, - - #[serde(flatten)] - pub tentative_accept_props: TentativeAcceptProperties, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - #[serde(flatten)] - pub activity_props: ActivityProperties, -} - -impl Object for TentativeAccept {} -impl ObjectExt for TentativeAccept { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for TentativeAccept { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Activity for TentativeAccept {} -impl ActivityExt for TentativeAccept { - fn props(&self) -> &ActivityProperties { - &self.activity_props - } - - fn props_mut(&mut self) -> &mut ActivityProperties { - &mut self.activity_props - } -} - -/// A specialization of Reject in which the rejection is considered tentative. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct TentativeReject { - #[serde(rename = "type")] - kind: TentativeRejectType, - - #[serde(flatten)] - pub tentative_reject_props: TentativeRejectProperties, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - #[serde(flatten)] - pub activity_props: ActivityProperties, -} - -impl Object for TentativeReject {} -impl ObjectExt for TentativeReject { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for TentativeReject { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Activity for TentativeReject {} -impl ActivityExt for TentativeReject { - fn props(&self) -> &ActivityProperties { - &self.activity_props - } - - fn props_mut(&mut self) -> &mut ActivityProperties { - &mut self.activity_props - } -} - -/// Indicates that the actor is traveling to target from origin. -/// -/// Travel is an IntransitiveObject whose actor specifies the direct object. If the target or -/// origin are not specified, either can be determined by context. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Travel { - #[serde(rename = "type")] - kind: TravelType, - - #[serde(flatten)] - pub travel_props: TravelProperties, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - #[serde(flatten)] - pub activity_props: ActivityProperties, -} - -impl Object for Travel {} -impl ObjectExt for Travel { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Travel { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Activity for Travel {} -impl ActivityExt for Travel { - fn props(&self) -> &ActivityProperties { - &self.activity_props - } - - fn props_mut(&mut self) -> &mut ActivityProperties { - &mut self.activity_props - } -} -impl IntransitiveActivity for Travel {} - -/// Indicates that the actor is undoing the object. -/// -/// In most cases, the object will be an Activity describing some previously performed action (for -/// instance, a person may have previously "liked" an article but, for whatever reason, might -/// choose to undo that like at some later point in time). -/// -/// The target and origin typically have no defined meaning. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Undo { - #[serde(rename = "type")] - kind: UndoType, - - #[serde(flatten)] - pub undo_props: UndoProperties, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - #[serde(flatten)] - pub activity_props: ActivityProperties, -} - -impl Object for Undo {} -impl ObjectExt for Undo { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Undo { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Activity for Undo {} -impl ActivityExt for Undo { - fn props(&self) -> &ActivityProperties { - &self.activity_props - } - - fn props_mut(&mut self) -> &mut ActivityProperties { - &mut self.activity_props - } -} - -/// Indicates that the actor has updated the object. -/// -/// Note, however, that this vocabulary does not define a mechanism for describing the actual set -/// of modifications made to object. -/// -/// The target and origin typically have no defined meaning. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Update { - #[serde(rename = "type")] - kind: UpdateType, - - #[serde(flatten)] - pub update_props: UpdateProperties, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - #[serde(flatten)] - pub activity_props: ActivityProperties, -} - -impl Object for Update {} -impl ObjectExt for Update { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Update { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Activity for Update {} -impl ActivityExt for Update { - fn props(&self) -> &ActivityProperties { - &self.activity_props - } - - fn props_mut(&mut self) -> &mut ActivityProperties { - &mut self.activity_props - } -} - -/// Indicates that the actor has viewed the object. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct View { - #[serde(rename = "type")] - kind: ViewType, - - #[serde(flatten)] - pub view_props: ViewProperties, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - #[serde(flatten)] - pub activity_props: ActivityProperties, -} - -impl Object for View {} -impl ObjectExt for View { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for View { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Activity for View {} -impl ActivityExt for View { - fn props(&self) -> &ActivityProperties { - &self.activity_props - } - - fn props_mut(&mut self) -> &mut ActivityProperties { - &mut self.activity_props - } -} diff --git a/server/src/activitypub/actor/mod.rs b/server/src/activitypub/actor/mod.rs deleted file mode 100644 index adb0b2d5c..000000000 --- a/server/src/activitypub/actor/mod.rs +++ /dev/null @@ -1,291 +0,0 @@ -/* - * This file is part of ActivityPub. - * - * Copyright © 2018 Riley Trautman - * - * ActivityPub is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ActivityPub is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ActivityPub. If not, see . - */ - -//! Actor traits and types - -use activitystreams_derive::Properties; -pub use activitystreams_traits::Actor; -pub use activitystreams_types::actor::kind; -use serde_derive::{Deserialize, Serialize}; - -pub mod properties; - -use self::{kind::*, properties::*}; -use activitypub::object::{ - properties::{ApObjectProperties, ObjectProperties}, - ApObjectExt, Object, ObjectExt, -}; - -/// The ActivityPub Actor Extension Trait -/// -/// This trait provides generic access to an activitypub actor's properties -pub trait ApActorExt: Actor { - fn props(&self) -> &ApActorProperties; - fn props_mut(&mut self) -> &mut ApActorProperties; -} - -/// Describes a software application. -#[derive(Clone, Debug, Default, Deserialize, Serialize, Properties)] -#[serde(rename_all = "camelCase")] -pub struct Application { - #[serde(rename = "type")] - kind: ApplicationType, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - pub object_props: ObjectProperties, - - /// Adds all valid activitypub object properties to this struct - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - /// Adds all valid activitypub actor properties to this struct - #[serde(flatten)] - pub ap_actor_props: ApActorProperties, -} - -impl Object for Application {} -impl ObjectExt for Application { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Application { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Actor for Application {} -impl ApActorExt for Application { - fn props(&self) -> &ApActorProperties { - &self.ap_actor_props - } - - fn props_mut(&mut self) -> &mut ApActorProperties { - &mut self.ap_actor_props - } -} - -/// Represents a formal or informal collective of Actors. -#[derive(Clone, Debug, Default, Deserialize, Serialize, Properties)] -#[serde(rename_all = "camelCase")] -pub struct Group { - #[serde(rename = "type")] - kind: GroupType, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - pub object_props: ObjectProperties, - - /// Adds all valid activitypub object properties to this struct - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - /// Adds all valid activitypub actor properties to this struct - #[serde(flatten)] - pub ap_actor_props: ApActorProperties, -} - -impl Object for Group {} -impl ObjectExt for Group { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Group { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Actor for Group {} -impl ApActorExt for Group { - fn props(&self) -> &ApActorProperties { - &self.ap_actor_props - } - - fn props_mut(&mut self) -> &mut ApActorProperties { - &mut self.ap_actor_props - } -} - -/// Represents an organization. -#[derive(Clone, Debug, Default, Deserialize, Serialize, Properties)] -#[serde(rename_all = "camelCase")] -pub struct Organization { - #[serde(rename = "type")] - kind: OrganizationType, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - pub object_props: ObjectProperties, - - /// Adds all valid activitypub object properties to this struct - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - /// Adds all valid activitypub actor properties to this struct - #[serde(flatten)] - pub ap_actor_props: ApActorProperties, -} - -impl Object for Organization {} -impl ObjectExt for Organization { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Organization { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Actor for Organization {} -impl ApActorExt for Organization { - fn props(&self) -> &ApActorProperties { - &self.ap_actor_props - } - - fn props_mut(&mut self) -> &mut ApActorProperties { - &mut self.ap_actor_props - } -} - -/// Represents an individual person. -#[derive(Clone, Debug, Default, Deserialize, Serialize, Properties)] -#[serde(rename_all = "camelCase")] -pub struct Person { - #[serde(rename = "type")] - kind: PersonType, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - pub object_props: ObjectProperties, - - /// Adds all valid activitypub object properties to this struct - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - /// Adds all valid activitypub actor properties to this struct - #[serde(flatten)] - pub ap_actor_props: ApActorProperties, -} - -impl Object for Person {} -impl ObjectExt for Person { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Person { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Actor for Person {} -impl ApActorExt for Person { - fn props(&self) -> &ApActorProperties { - &self.ap_actor_props - } - - fn props_mut(&mut self) -> &mut ApActorProperties { - &mut self.ap_actor_props - } -} - -/// Represents a service of any kind. -#[derive(Clone, Debug, Default, Deserialize, Serialize, Properties)] -#[serde(rename_all = "camelCase")] -pub struct Service { - #[serde(rename = "type")] - kind: ServiceType, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - pub object_props: ObjectProperties, - - /// Adds all valid activitypub object properties to this struct - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - /// Adds all valid activitypub actor properties to this struct - #[serde(flatten)] - pub ap_actor_props: ApActorProperties, -} - -impl Object for Service {} -impl ObjectExt for Service { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Service { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Actor for Service {} -impl ApActorExt for Service { - fn props(&self) -> &ApActorProperties { - &self.ap_actor_props - } - - fn props_mut(&mut self) -> &mut ApActorProperties { - &mut self.ap_actor_props - } -} diff --git a/server/src/activitypub/actor/properties.rs b/server/src/activitypub/actor/properties.rs deleted file mode 100644 index 9b7f38aa7..000000000 --- a/server/src/activitypub/actor/properties.rs +++ /dev/null @@ -1,129 +0,0 @@ -/* - * This file is part of ActivityPub. - * - * Copyright © 2018 Riley Trautman - * - * ActivityPub is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ActivityPub is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ActivityPub. If not, see . - */ - -//! Namespace for properties of standard Actor types -//! -//! To use these properties in your own types, you can flatten them into your struct with serde: -//! -//! ```rust -//! use activitypub::{Object, Actor, actor::properties::ApActorProperties}; -//! use serde_derive::{Deserialize, Serialize}; -//! -//! #[derive(Clone, Debug, Serialize, Deserialize)] -//! #[serde(rename_all = "camelCase")] -//! pub struct MyActor { -//! #[serde(rename = "type")] -//! pub kind: String, -//! -//! /// Define a require property for the MyActor type -//! pub my_property: String, -//! -//! #[serde(flatten)] -//! pub actor_props: ApActorProperties, -//! } -//! -//! impl Object for MyActor {} -//! impl Actor for MyActor {} -//! # -//! # fn main() {} -//! ``` - -use activitystreams_derive::Properties; -use serde_derive::{Deserialize, Serialize}; - -use crate::activitypub::endpoint::Endpoint; - -/// Define activitypub properties for the Actor type as described by the Activity Pub vocabulary. -#[derive(Clone, Debug, Default, Deserialize, Properties, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct ApActorProperties { - // TODO: IRI - /// A reference to an [[ActivityStreams](https://www.w3.org/ns/activitystreams)] - /// OrderedCollection comprised of all the messages received by the actor. - /// - /// - Range: `anyUri` - /// - Functional: true - #[activitystreams(concrete(String), functional)] - pub inbox: serde_json::Value, - - // TODO: IRI - /// An [ActivityStreams](https://www.w3.org/ns/activitystreams)] OrderedCollection comprised of - /// all the messages produced by the actor. - /// - /// - Range: `anyUri` - /// - Functional: true - #[activitystreams(concrete(String), functional)] - pub outbox: serde_json::Value, - - // TODO: IRI - /// A link to an [[ActivityStreams](https://www.w3.org/ns/activitystreams)] collection of the - /// actors that this actor is following. - /// - /// - Range: `anyUri` - /// - Functional: true - #[activitystreams(concrete(String), functional)] - pub following: Option, - - // TODO: IRI - /// A link to an [[ActivityStreams](https://www.w3.org/ns/activitystreams)] collection of the - /// actors that follow this actor. - /// - /// - Range: `anyUri` - /// - Functional: true - #[activitystreams(concrete(String), functional)] - pub followers: Option, - - // TODO: IRI - /// A link to an [[ActivityStreams](https://www.w3.org/ns/activitystreams)] collection of - /// objects this actor has liked. - /// - /// - Range: `anyUri` - /// - Functional: true - #[activitystreams(concrete(String), functional)] - pub liked: Option, - - // TODO: IRI - /// A list of supplementary Collections which may be of interest. - /// - /// - Range: `anyUri` - /// - Functional: false - #[serde(skip_serializing_if = "Option::is_none")] - #[activitystreams(concrete(String))] - pub streams: Option, - - /// A short username which may be used to refer to the actor, with no uniqueness guarantees. - /// - /// - Range: `anyUri` - /// - Functional: true - #[serde(skip_serializing_if = "Option::is_none")] - #[activitystreams(concrete(String), functional)] - pub preferred_username: Option, - - /// A json object which maps additional (typically server/domain-wide) endpoints which may be - /// useful either for this actor or someone referencing this actor. - /// - /// This mapping may be nested inside the actor document as the value or may be a link to a - /// JSON-LD document with these properties. - /// - /// - Range: `Endpoint` - /// - Functional: true - #[serde(skip_serializing_if = "Option::is_none")] - #[activitystreams(concrete(Endpoint), functional)] - pub endpoints: Option, -} diff --git a/server/src/activitypub/collection.rs b/server/src/activitypub/collection.rs deleted file mode 100644 index 8a25cce44..000000000 --- a/server/src/activitypub/collection.rs +++ /dev/null @@ -1,264 +0,0 @@ -/* - * This file is part of ActivityPub. - * - * Copyright © 2018 Riley Trautman - * - * ActivityPub is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ActivityPub is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ActivityPub. If not, see . - */ - -//! Collection traits and types - -use activitystreams_derive::Properties; -pub use activitystreams_traits::{Collection, CollectionPage}; -pub use activitystreams_types::collection::{kind, properties, CollectionExt, CollectionPageExt}; -use serde_derive::{Deserialize, Serialize}; - -use self::{kind::*, properties::*}; -use activitypub::object::{ - properties::{ApObjectProperties, ObjectProperties}, - ApObjectExt, Object, ObjectExt, -}; - -/// The default `Collection` type. -#[derive(Clone, Debug, Default, Deserialize, Serialize, Properties)] -#[serde(rename_all = "camelCase")] -pub struct UnorderedCollection { - #[serde(rename = "type")] - kind: CollectionType, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - pub object_props: ObjectProperties, - - /// Adds all valid ap object properties to this struct - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - /// Adds all valid collection properties to this struct - #[serde(flatten)] - pub collection_props: CollectionProperties, -} - -impl Object for UnorderedCollection {} -impl ObjectExt for UnorderedCollection { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for UnorderedCollection { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Collection for UnorderedCollection {} -impl CollectionExt for UnorderedCollection { - fn props(&self) -> &CollectionProperties { - &self.collection_props - } - - fn props_mut(&mut self) -> &mut CollectionProperties { - &mut self.collection_props - } -} - -/// Used to represent distinct subsets of items from a `Collection`. -#[derive(Clone, Debug, Default, Deserialize, Serialize, Properties)] -#[serde(rename_all = "camelCase")] -pub struct UnorderedCollectionPage { - #[serde(rename = "type")] - kind: CollectionPageType, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - pub object_props: ObjectProperties, - - /// Adds all valid ap object properties to this struct - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - /// Adds all valid collection properties to this struct - #[serde(flatten)] - pub collection_props: CollectionProperties, - - /// Adds all valid collection page properties to this struct - #[serde(flatten)] - pub collection_page_props: CollectionPageProperties, -} - -impl Object for UnorderedCollectionPage {} -impl ObjectExt for UnorderedCollectionPage { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for UnorderedCollectionPage { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Collection for UnorderedCollectionPage {} -impl CollectionExt for UnorderedCollectionPage { - fn props(&self) -> &CollectionProperties { - &self.collection_props - } - - fn props_mut(&mut self) -> &mut CollectionProperties { - &mut self.collection_props - } -} -impl CollectionPage for UnorderedCollectionPage {} -impl CollectionPageExt for UnorderedCollectionPage { - fn props(&self) -> &CollectionPageProperties { - &self.collection_page_props - } - - fn props_mut(&mut self) -> &mut CollectionPageProperties { - &mut self.collection_page_props - } -} - -/// A subtype of `Collection` in which members of the logical collection are assumed to always be -/// strictly ordered. -#[derive(Clone, Debug, Default, Deserialize, Serialize, Properties)] -#[serde(rename_all = "camelCase")] -pub struct OrderedCollection { - #[serde(rename = "type")] - kind: OrderedCollectionType, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - pub object_props: ObjectProperties, - - /// Adds all valid ap object properties to this struct - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - /// Adds all valid collection properties to this struct - #[serde(flatten)] - pub collection_props: CollectionProperties, -} - -impl Object for OrderedCollection {} -impl ObjectExt for OrderedCollection { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for OrderedCollection { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Collection for OrderedCollection {} -impl CollectionExt for OrderedCollection { - fn props(&self) -> &CollectionProperties { - &self.collection_props - } - - fn props_mut(&mut self) -> &mut CollectionProperties { - &mut self.collection_props - } -} - -/// Used to represent ordered subsets of items from an `OrderedCollection`. -#[derive(Clone, Debug, Default, Deserialize, Serialize, Properties)] -#[serde(rename_all = "camelCase")] -pub struct OrderedCollectionPage { - #[serde(rename = "type")] - kind: OrderedCollectionPageType, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - pub object_props: ObjectProperties, - - /// Adds all valid ap object properties to this struct - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - /// Adds all valid collection properties to this struct - #[serde(flatten)] - pub collection_props: CollectionProperties, - - /// Adds all valid collection page properties to this struct - #[serde(flatten)] - pub collection_page_props: CollectionPageProperties, - - /// Adds all valid ordered collection page properties to this struct - #[serde(flatten)] - pub ordered_collection_page_props: OrderedCollectionPageProperties, -} - -impl Object for OrderedCollectionPage {} -impl ObjectExt for OrderedCollectionPage { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for OrderedCollectionPage { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} -impl Collection for OrderedCollectionPage {} -impl CollectionExt for OrderedCollectionPage { - fn props(&self) -> &CollectionProperties { - &self.collection_props - } - - fn props_mut(&mut self) -> &mut CollectionProperties { - &mut self.collection_props - } -} -impl CollectionPage for OrderedCollectionPage {} -impl CollectionPageExt for OrderedCollectionPage { - fn props(&self) -> &CollectionPageProperties { - &self.collection_page_props - } - - fn props_mut(&mut self) -> &mut CollectionPageProperties { - &mut self.collection_page_props - } -} diff --git a/server/src/activitypub/endpoint.rs b/server/src/activitypub/endpoint.rs deleted file mode 100644 index 639860cf6..000000000 --- a/server/src/activitypub/endpoint.rs +++ /dev/null @@ -1,107 +0,0 @@ -/* - * This file is part of ActivityPub. - * - * Copyright © 2018 Riley Trautman - * - * ActivityPub is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ActivityPub is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ActivityPub. If not, see . - */ - -//! Endpoint traits and types - -use activitystreams_derive::Properties; -use serde_derive::{Deserialize, Serialize}; - -/// A json object which maps additional (typically server/domain-wide) endpoints which may be -/// useful either for this actor or someone referencing this actor. -/// -/// This mapping may be nested inside the actor document as the value or may be a link to a JSON-LD -/// document with these properties. -#[derive(Clone, Debug, Default, Deserialize, Properties, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Endpoint { - // TODO: IRI - /// Endpoint URI so this actor's clients may access remote ActivityStreams objects which - /// require authentication to access. - /// - /// To use this endpoint, the client posts an x-www-form-urlencoded id parameter with the value - /// being the id of the requested ActivityStreams object. - /// - /// - Range: `anyUri` - /// - Functional: true - #[serde(skip_serializing_if = "Option::is_none")] - #[activitystreams(concrete(String))] - pub proxy_url: Option, - - // TODO: IRI - /// If OAuth 2.0 bearer tokens [[RFC6749](https://tools.ietf.org/html/rfc6749)] - /// [[RFC6750](https://tools.ietf.org/html/rfc6750)] are being used for authenticating client - /// to server interactions, this endpoint specifies a URI at which a browser-authenticated user - /// may obtain a new authorization grant. - /// - /// - Range: `anyUri` - /// - Functional: true - #[serde(skip_serializing_if = "Option::is_none")] - #[activitystreams(concrete(String))] - pub oauth_authorization_endpoint: Option, - - // TODO: IRI - /// If OAuth 2.0 bearer tokens [[RFC6749](https://tools.ietf.org/html/rfc6749)] - /// [[RFC6750](https://tools.ietf.org/html/rfc6750)] are being used for authenticating client - /// to server interactions, this endpoint specifies a URI at which a client may acquire an - /// access token. - /// - /// - Range: `anyUri` - /// - Functional: true - #[serde(skip_serializing_if = "Option::is_none")] - #[activitystreams(concrete(String))] - pub oauth_token_endpoint: Option, - - // TODO: IRI - /// If Linked Data Signatures and HTTP Signatures are being used for authentication and - /// authorization, this endpoint specifies a URI at which browser-authenticated users may - /// authorize a client's public key for client to server interactions. - /// - /// - Range: `anyUri` - /// - Functional: true - #[serde(skip_serializing_if = "Option::is_none")] - #[activitystreams(concrete(String))] - pub provide_client_key: Option, - - // TODO: IRI - /// If Linked Data Signatures and HTTP Signatures are being used for authentication and - /// authorization, this endpoint specifies a URI at which a client key may be signed by the - /// actor's key for a time window to act on behalf of the actor in interacting with foreign - /// servers. - /// - /// - Range: `anyUri` - /// - Functional: true - #[serde(skip_serializing_if = "Option::is_none")] - #[activitystreams(concrete(String))] - pub sign_client_key: Option, - - // TODO: IRI - /// An optional endpoint used for wide delivery of publicly addressed activities and activities - /// sent to followers. - /// - /// `shared_inbox`endpoints SHOULD also be publicly readable `OrderedCollection` objects - /// containing objects addressed to the Public special collection. Reading from the - /// `shared_inbox` endpoint MUST NOT present objects which are not addressed to the Public - /// endpoint. - /// - /// - Range: `anyUri` - /// - Functional: true - #[serde(skip_serializing_if = "Option::is_none")] - #[activitystreams(concrete(String))] - pub shared_inbox: Option, -} diff --git a/server/src/activitypub/link.rs b/server/src/activitypub/link.rs deleted file mode 100644 index 5de09fd88..000000000 --- a/server/src/activitypub/link.rs +++ /dev/null @@ -1,23 +0,0 @@ -/* - * This file is part of ActivityPub. - * - * Copyright © 2018 Riley Trautman - * - * ActivityPub is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ActivityPub is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ActivityPub. If not, see . - */ - -//! Link traits and types - -pub use activitystreams_traits::Link; -pub use activitystreams_types::link::{kind, properties, LinkExt, Mention}; diff --git a/server/src/activitypub/mod.rs b/server/src/activitypub/mod.rs deleted file mode 100644 index 9e990329c..000000000 --- a/server/src/activitypub/mod.rs +++ /dev/null @@ -1,57 +0,0 @@ -/* - * This file is part of ActivityPub. - * - * Copyright © 2018 Riley Trautman - * - * ActivityPub is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ActivityPub is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ActivityPub. If not, see . - */ - -//! ActivityPub -//! -//! This crate defines the base set of types from the ActivityPub specification. -//! -//! ## Example Usage -//! ```rust -//! use activitypub::{context, object::Video}; -//! use anyhow::Error; -//! -//! fn run() -> Result<(), Error> { -//! let mut video = Video::default(); -//! video.object_props.set_context_object(context())?; -//! video.ap_object_props.set_likes_string("https://my-instance.com/likes".to_owned()); -//! -//! let video_string = serde_json::to_string(&video)?; -//! -//! let video: Video = serde_json::from_str(&video_string)?; -//! -//! Ok(()) -//! } -//! ``` -pub mod activity; -pub mod actor; -pub mod collection; -mod endpoint; -pub mod link; -pub mod object; - -pub use self::{ - activity::{Activity, IntransitiveActivity}, - actor::Actor, - collection::{Collection, CollectionPage}, - endpoint::Endpoint, - link::Link, - object::Object, -}; -pub use activitystreams_traits::{properties, Error, Result}; -pub use activitystreams_types::{context, ContextObject, CustomLink, CustomObject}; diff --git a/server/src/activitypub/object/mod.rs b/server/src/activitypub/object/mod.rs deleted file mode 100644 index 07aa99e80..000000000 --- a/server/src/activitypub/object/mod.rs +++ /dev/null @@ -1,444 +0,0 @@ -/* - * This file is part of ActivityPub. - * - * Copyright © 2018 Riley Trautman - * - * ActivityPub is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ActivityPub is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ActivityPub. If not, see . - */ - -//! Object traits and types - -pub use activitystreams_traits::Object; -pub use activitystreams_types::object::{kind, ObjectExt}; -use serde_derive::{Deserialize, Serialize}; - -pub mod properties; - -use self::{kind::*, properties::*}; - -/// The ActivityPub Object Extension Trait -/// -/// This trait provides generic access to an activitypub object's properties -pub trait ApObjectExt: Object { - fn props(&self) -> &ApObjectProperties; - fn props_mut(&mut self) -> &mut ApObjectProperties; -} - -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Article { - #[serde(rename = "type")] - kind: ArticleType, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, -} - -impl Object for Article {} -impl ObjectExt for Article { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Article { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} - -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Audio { - #[serde(rename = "type")] - kind: AudioType, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, -} - -impl Object for Audio {} -impl ObjectExt for Audio { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Audio { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} - -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Document { - #[serde(rename = "type")] - kind: DocumentType, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, -} - -impl Object for Document {} -impl ObjectExt for Document { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Document { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} - -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Event { - #[serde(rename = "type")] - kind: EventType, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, -} - -impl Object for Event {} -impl ObjectExt for Event { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Event { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} - -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Image { - #[serde(rename = "type")] - kind: ImageType, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, -} - -impl Object for Image {} -impl ObjectExt for Image { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Image { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} - -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Note { - #[serde(rename = "type")] - kind: NoteType, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, -} - -impl Object for Note {} -impl ObjectExt for Note { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Note { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} - -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Page { - #[serde(rename = "type")] - kind: PageType, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, -} - -impl Object for Page {} -impl ObjectExt for Page { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Page { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} - -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Place { - #[serde(rename = "type")] - kind: PlaceType, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - #[serde(flatten)] - pub place_props: PlaceProperties, -} - -impl Object for Place {} -impl ObjectExt for Place { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Place { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} - -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Profile { - #[serde(rename = "type")] - kind: ProfileType, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - #[serde(flatten)] - pub profile_props: ProfileProperties, -} - -impl Object for Profile {} -impl ObjectExt for Profile { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Profile { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} - -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Relationship { - #[serde(rename = "type")] - kind: RelationshipType, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - #[serde(flatten)] - pub relationship_props: RelationshipProperties, -} - -impl Object for Relationship {} -impl ObjectExt for Relationship { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Relationship { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} - -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Tombstone { - #[serde(rename = "type")] - kind: TombstoneType, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, - - #[serde(flatten)] - pub tombstone_props: TombstoneProperties, -} - -impl Object for Tombstone {} -impl ObjectExt for Tombstone { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Tombstone { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} - -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Video { - #[serde(rename = "type")] - kind: VideoType, - - #[serde(flatten)] - pub object_props: ObjectProperties, - - #[serde(flatten)] - pub ap_object_props: ApObjectProperties, -} - -impl Object for Video {} -impl ObjectExt for Video { - fn props(&self) -> &ObjectProperties { - &self.object_props - } - - fn props_mut(&mut self) -> &mut ObjectProperties { - &mut self.object_props - } -} -impl ApObjectExt for Video { - fn props(&self) -> &ApObjectProperties { - &self.ap_object_props - } - - fn props_mut(&mut self) -> &mut ApObjectProperties { - &mut self.ap_object_props - } -} diff --git a/server/src/activitypub/object/properties.rs b/server/src/activitypub/object/properties.rs deleted file mode 100644 index ea4f004f4..000000000 --- a/server/src/activitypub/object/properties.rs +++ /dev/null @@ -1,111 +0,0 @@ -/* - * This file is part of ActivityPub. - * - * Copyright © 2018 Riley Trautman - * - * ActivityPub is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ActivityPub is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ActivityPub. If not, see . - */ - -//! Namespace for properties of standard Object types -//! -//! To use these properties in your own types, you can flatten them into your struct with serde: -//! -//! ```rust -//! use activitypub::{Object, object::properties::ApObjectProperties}; -//! use serde_derive::{Deserialize, Serialize}; -//! -//! #[derive(Clone, Debug, Serialize, Deserialize)] -//! #[serde(rename_all = "camelCase")] -//! pub struct MyObject { -//! #[serde(rename = "type")] -//! pub kind: String, -//! -//! /// Define a require property for the MyObject type -//! pub my_property: String, -//! -//! #[serde(flatten)] -//! pub object_props: ApObjectProperties, -//! } -//! -//! impl Object for MyObject {} -//! # -//! # fn main() {} -//! ``` - -use super::Object; - -use activitystreams_derive::Properties; -pub use activitystreams_types::object::properties::{ - ObjectProperties, PlaceProperties, ProfileProperties, RelationshipProperties, TombstoneProperties, -}; -use serde_derive::{Deserialize, Serialize}; - -/// Define activitypub properties for the Object type as described by the Activity Pub vocabulary. -#[derive(Clone, Debug, Default, Deserialize, Properties, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct ApObjectProperties { - // TODO: IRI - /// This is a list of all Announce activities with this object as the object property, added as - /// a side effect. - /// - /// The shares collection MUST be either an OrderedCollection or a Collection and MAY be - /// filtered on privileges of an authenticated user or as appropriate when no authentication is - /// given. - /// - /// - Range: `anyUri` - /// - Functional: true - #[serde(skip_serializing_if = "Option::is_none")] - #[activitystreams(concrete(String), functional)] - pub shares: Option, - - /// This is a list of all Like activities with this object as the object property, added as a - /// side effect. - /// - /// The likes collection MUST be either an OrderedCollection or a Collection and MAY be - /// filtered on privileges of an authenticated user or as appropriate when no authentication is - /// given. - /// - /// - Range: `anyUri` - /// - Functional: true - #[serde(skip_serializing_if = "Option::is_none")] - #[activitystreams(concrete(String), functional)] - pub likes: Option, - - /// The source property is intended to convey some sort of source from which the content markup - /// was derived, as a form of provenance, or to support future editing by clients. - /// - /// In general, clients do the conversion from source to content, not the other way around. - /// - /// The value of source is itself an object which uses its own content and mediaType fields to - /// supply source information. - /// - /// - Range: `Object` - /// - Functional: true - #[serde(skip_serializing_if = "Option::is_none")] - #[activitystreams(ab(Object), concrete(String), functional)] - pub source: Option, - - /// Servers MAY support uploading document types to be referenced in activites, such as images, - /// video or other binary data, but the precise mechanism is out of scope for this version of - /// `ActivityPub`. - /// - /// The Social Web Community Group is refining the protocol in the - /// [`ActivityPub` Media Upload report](https://www.w3.org/wiki/SocialCG/ActivityPub/MediaUpload). - /// - /// - Range: `anyUri` - /// - Functional: false - #[serde(skip_serializing_if = "Option::is_none")] - #[activitystreams(concrete(String))] - pub upload_media: Option, -} diff --git a/server/src/apub/community.rs b/server/src/apub/community.rs index 79eb68398..ee3edc1f4 100644 --- a/server/src/apub/community.rs +++ b/server/src/apub/community.rs @@ -1,9 +1,9 @@ -use crate::activitypub::{actor::Group, collection::UnorderedCollection, context}; use crate::apub::make_apub_endpoint; use crate::db::community::Community; use crate::db::community_view::CommunityFollowerView; use crate::db::establish_unpooled_connection; use crate::to_datetime_utc; +use activitypub::{actor::Group, collection::UnorderedCollection, context}; use actix_web::body::Body; use actix_web::web::Path; use actix_web::HttpResponse; diff --git a/server/src/apub/post.rs b/server/src/apub/post.rs index 50b87c873..ebb171290 100644 --- a/server/src/apub/post.rs +++ b/server/src/apub/post.rs @@ -1,7 +1,7 @@ -use crate::activitypub::{context, object::Page}; use crate::apub::make_apub_endpoint; use crate::db::post::Post; use crate::to_datetime_utc; +use activitypub::{context, object::Page}; impl Post { pub fn as_page(&self) -> Page { diff --git a/server/src/apub/puller.rs b/server/src/apub/puller.rs index 3ab4c69bd..93fa99e72 100644 --- a/server/src/apub/puller.rs +++ b/server/src/apub/puller.rs @@ -1,12 +1,12 @@ extern crate reqwest; use self::reqwest::Error; -use crate::activitypub::actor::Group; use crate::api::community::{GetCommunityResponse, ListCommunitiesResponse}; use crate::api::post::GetPosts; use crate::db::community_view::CommunityView; use crate::naive_now; use crate::settings::Settings; +use activitypub::actor::Group; use serde_json::Value; // TODO: right now all of the data is requested on demand, for production we will need to store diff --git a/server/src/apub/user.rs b/server/src/apub/user.rs index fb31e4b68..5f2421f11 100644 --- a/server/src/apub/user.rs +++ b/server/src/apub/user.rs @@ -1,8 +1,8 @@ -use crate::activitypub::{actor::Person, context}; use crate::apub::make_apub_endpoint; use crate::db::establish_unpooled_connection; use crate::db::user::User_; use crate::to_datetime_utc; +use activitypub::{actor::Person, context}; use actix_web::body::Body; use actix_web::web::Path; use actix_web::HttpResponse; diff --git a/server/src/lib.rs b/server/src/lib.rs index 5b37a08e6..3e22585de 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -22,7 +22,6 @@ pub extern crate serde_json; pub extern crate sha2; pub extern crate strum; -pub mod activitypub; pub mod api; pub mod apub; pub mod db; From 1a9c1677d3a06032f360e21ae2ab58cebc5128c6 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Thu, 5 Mar 2020 10:02:23 -0500 Subject: [PATCH 101/164] Adding hr separator for top level comment groups on mobile. --- ui/src/components/comment-node.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index 9b476c7eb..19c834237 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -122,6 +122,9 @@ export class CommentNode extends Component { node.comment.parent_id && !this.props.noIndent ? 'ml-2' : '' }`} > + {!node.comment.parent_id && !this.props.noIndent && ( +
                + )}
                { style={ !this.props.noIndent && this.props.node.comment.parent_id && - `border-left: 1px solid; border-color: ${this.state.borderColor} !important` + `border-left: 1px ${this.state.borderColor} solid !important` } >
                { )}
                + {/* end of details */} {this.state.showRemoveDialog && ( Date: Thu, 5 Mar 2020 16:02:53 +0100 Subject: [PATCH 102/164] comment --- server/src/apub/puller.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/apub/puller.rs b/server/src/apub/puller.rs index 93fa99e72..b68779820 100644 --- a/server/src/apub/puller.rs +++ b/server/src/apub/puller.rs @@ -69,7 +69,7 @@ pub fn get_remote_community(identifier: String) -> Result Date: Thu, 5 Mar 2020 10:18:48 -0500 Subject: [PATCH 103/164] Thinner blockquotes. Corrected user on private messaging. --- ui/assets/css/main.css | 2 +- ui/src/components/private-message-form.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/assets/css/main.css b/ui/assets/css/main.css index 64f086b31..53237793f 100644 --- a/ui/assets/css/main.css +++ b/ui/assets/css/main.css @@ -117,7 +117,7 @@ } blockquote { - border-left: 3px solid #ccc; + border-left: 1px solid var(--secondary); margin: 0.5em 5px; padding: 0.1em 5px; } diff --git a/ui/src/components/private-message-form.tsx b/ui/src/components/private-message-form.tsx index 13b4d2eab..90b3b6e16 100644 --- a/ui/src/components/private-message-form.tsx +++ b/ui/src/components/private-message-form.tsx @@ -126,7 +126,7 @@ export class PrivateMessageForm extends Component< {this.state.recipient && (
                {this.state.recipient.avatar && showAvatars() && ( From 858366c57b959c0b2bc6e2a43ca194084a1ff0d8 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Thu, 5 Mar 2020 15:10:46 -0500 Subject: [PATCH 104/164] Proper comment-node depth coloring. --- ui/src/components/comment-node.tsx | 6 ++++-- ui/src/components/post.tsx | 15 +++++++++++++-- ui/src/interfaces.ts | 1 + ui/src/utils.ts | 2 ++ 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index 19c834237..db3c589d1 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -27,7 +27,7 @@ import { pictshareAvatarThumbnail, showAvatars, setupTippy, - randomHsl, + colorList, } from '../utils'; import moment from 'moment'; import { MomentTime } from './moment-time'; @@ -94,7 +94,9 @@ export class CommentNode extends Component { score: this.props.node.comment.score, upvotes: this.props.node.comment.upvotes, downvotes: this.props.node.comment.downvotes, - borderColor: randomHsl(), + borderColor: this.props.node.comment.depth + ? colorList[this.props.node.comment.depth % colorList.length] + : colorList[0], }; constructor(props: any, context: any) { diff --git a/ui/src/components/post.tsx b/ui/src/components/post.tsx index faee23ed0..e6b4a206d 100644 --- a/ui/src/components/post.tsx +++ b/ui/src/components/post.tsx @@ -311,16 +311,27 @@ export class Post extends Component { } let tree: Array = []; for (let comment of this.state.comments) { + let child = map.get(comment.id); if (comment.parent_id) { - map.get(comment.parent_id).children.push(map.get(comment.id)); + let parent_ = map.get(comment.parent_id); + parent_.children.push(child); } else { - tree.push(map.get(comment.id)); + tree.push(child); } + + this.setDepth(child); } return tree; } + setDepth(node: CommentNodeI, i: number = 0): void { + for (let child of node.children) { + child.comment.depth = i; + this.setDepth(child, i + 1); + } + } + commentsTree() { let nodes = this.buildCommentsTree(); return ( diff --git a/ui/src/interfaces.ts b/ui/src/interfaces.ts index 5baadb170..eb58ca116 100644 --- a/ui/src/interfaces.ts +++ b/ui/src/interfaces.ts @@ -208,6 +208,7 @@ export interface Comment { saved?: boolean; user_mention_id?: number; // For mention type recipient_id?: number; + depth?: number; } export interface Category { diff --git a/ui/src/utils.ts b/ui/src/utils.ts index bdd629c77..058be6ae3 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -768,6 +768,8 @@ export function postSort( } } +export const colorList: Array = [...Array(10)].map(() => randomHsl()); + export function randomHsl() { return `hsla(${Math.random() * 360}, 100%, 50%, 1)`; } From 876d3117062f3f801026d72c9359613eca7e46ed Mon Sep 17 00:00:00 2001 From: Dessalines Date: Thu, 5 Mar 2020 15:46:33 -0500 Subject: [PATCH 105/164] Remove email from GetUserDetails when not same user. Fixes #579 --- server/src/api/user.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/server/src/api/user.rs b/server/src/api/user.rs index 99072a749..1d332b906 100644 --- a/server/src/api/user.rs +++ b/server/src/api/user.rs @@ -466,7 +466,7 @@ impl Perform for Oper { } }; - let user_view = UserView::read(&conn, user_details_id)?; + let mut user_view = UserView::read(&conn, user_details_id)?; let mut posts_query = PostQueryBuilder::create(&conn) .sort(&sort) @@ -502,6 +502,15 @@ impl Perform for Oper { let creator_user = admins.remove(creator_index); admins.insert(0, creator_user); + // If its not the same user, remove the email + if let Some(user_id) = user_id { + if user_details_id != user_id { + user_view.email = None; + } + } else { + user_view.email = None; + } + // Return the jwt Ok(GetUserDetailsResponse { user: user_view, From 6af8186e77c1e018ee4f9c4123de00803194c44c Mon Sep 17 00:00:00 2001 From: Felipe Forte Date: Fri, 6 Mar 2020 10:58:36 +0000 Subject: [PATCH 106/164] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (230 of 230 strings) Translation: Lemmy/lemmy Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/pt_BR/ Translated using Weblate (Portuguese (Brazil)) Currently translated at 99.5% (229 of 230 strings) Translation: Lemmy/lemmy Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/pt_BR/ --- ui/translations/pt_BR.json | 39 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/ui/translations/pt_BR.json b/ui/translations/pt_BR.json index 732ab6325..390fddc16 100644 --- a/ui/translations/pt_BR.json +++ b/ui/translations/pt_BR.json @@ -64,8 +64,7 @@ "delete": "apagar", "deleted": "apagado", "delete_account": "Apagar conta", - "delete_account_confirm": - "Aviso: isso vai apagar seus dados de forma permanente. Escreva sua senha para confirmar.", + "delete_account_confirm": "Aviso: isso vai apagar seus dados de forma permanente. Escreva sua senha para confirmar.", "restore": "restaurar", "ban": "banir", "ban_from_site": "banido do site", @@ -122,8 +121,7 @@ "login_sign_up": "Entrar / Inscrever-se", "login": "Entrar", "sign_up": "Inscrever-se", - "notifications_error": - "Seu navegador não oferece notificações para a área de trabalho. Tente o Firefox ou o Chrome.", + "notifications_error": "Seu navegador não oferece notificações para a área de trabalho. Tente o Firefox ou o Chrome.", "unread_messages": "Mensagens não lidas", "messages": "Mensagens", "password": "Senha", @@ -136,8 +134,7 @@ "no_email_setup": "Esse servidor não configurou corretamente o e-mail.", "email": "E-mail", "matrix_user_id": "Usuário Matrix", - "private_message_disclaimer": - "Aviso: mensagens privadas no Lemmy não são seguras. Crie uma conta em <1>Riot.im para troca segura de mensagens.", + "private_message_disclaimer": "Aviso: mensagens privadas no Lemmy não são seguras. Crie uma conta em <1>Riot.im para troca segura de mensagens.", "send_notifications_to_email": "Enviar notificações para o e-mail", "optional": "Opcional", "expires": "Expira", @@ -167,15 +164,13 @@ "theme": "Tema", "sponsors": "Patrocinadores", "sponsors_of_lemmy": "Patrocinadores do Lemmy", - "sponsor_message": - "Lemmy é um programa livre e de código aberto, o que significa que não haverá publicidade, monetização ou capital de risco, jamais. Suas doações apoiam de forma direta o desenvolvimento em tempo integral do projeto. Muitos agradecimentos às sequintes pessoas:", + "sponsor_message": "Lemmy é um programa livre e de código aberto, o que significa que não haverá publicidade, monetização ou capital de risco, jamais. Suas doações apoiam de forma direta o desenvolvimento em tempo integral do projeto. Muitos agradecimentos às sequintes pessoas:", "support_on_patreon": "Colabore no Patreon", "support_on_liberapay": "Colabore no Liberapay", "donate_to_lemmy": "Faça uma doação ao Lemmy", "donate": "Doar", - "general_sponsors": - "Patrocinadores são aqueles que doaram entre $10 e $39 ao Lemmy.", - "crypto": "Crypto", + "general_sponsors": "Patrocinadores são aqueles que doaram entre $10 e $39 ao Lemmy.", + "crypto": "Cryptomoedas", "bitcoin": "Bitcoin", "ethereum": "Ethereum", "monero": "Monero", @@ -190,8 +185,7 @@ "yes": "sim", "no": "não", "powered_by": "Powered by", - "landing_0": - "Lemmy é um <1>agregador de links / alternativa ao reddit, com a intenção de funcionar junto ao <2>fediverso.<3>Pode ser hospedado em servidor próprio, tem atualização de comentários em tempo real e é minúsculo (<4>~80kB). A federação com a rede ActivityPub está no roteiro do projeto. <5>Esta é uma <6>versão beta bastante antecipada, e muitas funcionalidades ainda estão quebradas ou ausentes. <7>Sugira novas funcionalidades ou reporte erros <8>aqui.<9>Feito com <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.", + "landing_0": "Lemmy é um <1>agregador de links / alternativa ao reddit, com a intenção de funcionar junto ao <2>fediverso.<3>Pode ser hospedado em servidor próprio, tem atualização de comentários em tempo real e é minúsculo (<4>~80kB). A federação com a rede ActivityPub está no roteiro do projeto. <5>Esta é uma <6>versão beta bastante antecipada, e muitas funcionalidades ainda estão quebradas ou ausentes. <7>Sugira novas funcionalidades ou reporte erros <8>aqui.<9>Feito com <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.", "not_logged_in": "Não autenticado.", "logged_in": "Autenticado.", "community_ban": "Você foi banido desta comunidade.", @@ -200,14 +194,13 @@ "couldnt_like_comment": "Não foi possível curtir o comentário.", "couldnt_update_comment": "Não foi possível atualizar o comentário.", "couldnt_save_comment": "Não foi possível guardar o comentário.", - "no_comment_edit_allowed": "Sem permissão para editar de comentário.", + "no_comment_edit_allowed": "Sem permissão para editar comentário.", "no_post_edit_allowed": "Sem permissão para editar publicação.", "no_community_edit_allowed": "Sem permissão para editar comunidade.", "couldnt_find_community": "Não foi possível encontrar a comunidade.", "couldnt_update_community": "Não foi possível atualizar a comunidade.", "community_already_exists": "Esta comunidade já existe.", - "community_moderator_already_exists": - "Este moderador da comunidade já existe.", + "community_moderator_already_exists": "Este moderador da comunidade já existe.", "community_follower_already_exists": "Este seguidor da comunidade já existe.", "community_user_already_banned": "Este usuário da comunidade já foi banido.", "couldnt_create_post": "Não foi possível criar a publicação.", @@ -220,8 +213,7 @@ "not_an_admin": "Não é administrador.", "site_already_exists": "O site já existe.", "couldnt_update_site": "Não foi possível atualizar o site.", - "couldnt_find_that_username_or_email": - "Não foi possível encontrar esse usuário ou e-mail.", + "couldnt_find_that_username_or_email": "Não foi possível encontrar esse usuário ou e-mail.", "password_incorrect": "Senha incorreta.", "passwords_dont_match": "As senhas não são iguais.", "admin_already_created": "Desculpe, já há um administrador.", @@ -230,10 +222,11 @@ "couldnt_update_user": "Não foi possível atualizar o usuário.", "system_err_login": "Erro no sistema. Tente sair e autenticar-se outra vez.", "couldnt_create_private_message": "Não foi possível criar mensagem privada.", - "no_private_message_edit_allowed": - "Sem permissão para editar mensagem privada.", - "couldnt_update_private_message": - "Não foi possível atualizar a mensagem privada.", + "no_private_message_edit_allowed": "Sem permissão para editar mensagem privada.", + "couldnt_update_private_message": "Não foi possível atualizar a mensagem privada.", "time": "Tempo", - "action": "Ação" + "action": "Ação", + "more": "mais", + "couldnt_get_comments": "Não foi possível receber os comentários.", + "post_title_too_long": "Título da publicação muito longo." } From 548c6697d03a09e2337f58977b18f44bb836bf85 Mon Sep 17 00:00:00 2001 From: Riccardo Mazzon Date: Fri, 6 Mar 2020 10:58:36 +0000 Subject: [PATCH 107/164] Translated using Weblate (Italian) Currently translated at 91.7% (211 of 230 strings) Translation: Lemmy/lemmy Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/it/ --- ui/translations/it.json | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/ui/translations/it.json b/ui/translations/it.json index 50e621d93..d047bb166 100644 --- a/ui/translations/it.json +++ b/ui/translations/it.json @@ -55,7 +55,7 @@ "delete": "cancella", "deleted": "cancellato", "delete_account": "Cancella Account", - "delete_account_confirm": "Attenzione: stai per cancellare permanentemente tutti i tuoi dati. Sei sicuro?", + "delete_account_confirm": "Attenzione: stai per cancellare permanentemente tutti i tuoi dati. Inserisci la tua password per confermare questa azione.", "restore": "ripristina", "ban": "ban", "ban_from_site": "banna dal sito", @@ -186,5 +186,28 @@ "admin_already_created": "Spiacente, esiste già un amministratore.", "user_already_exists": "L'utente esiste già.", "couldnt_update_user": "Impossibile aggiornare l'utente.", - "system_err_login": "Si è verificato un errore. Prova ad effettuare nuovamente il login." + "system_err_login": "Si è verificato un errore. Prova ad effettuare nuovamente il login.", + "more": "altro", + "message": "Messaggio", + "avatar": "Avatar", + "upload_avatar": "Carica Avatar", + "docs": "Documentazione", + "message_sent": "Messaggio inviato", + "messages": "Messaggi", + "show_avatars": "Mostra Avatar", + "old_password": "Vecchia Password", + "forgot_password": "password dimenticata", + "new_password": "Nuova Password", + "private_message_disclaimer": "Attenzione: i messaggi privati su Lemmy non sono sicuri. Crea un account su <1>Riot.im per una messaggistica sicura.", + "language": "Lingua", + "enable_downvotes": "Abilita Downvote", + "enable_nsfw": "Abilita NSFW", + "donate_to_lemmy": "Dona a Lemmy", + "donate": "Dona", + "from": "da", + "archive_link": "link archivio", + "matrix_user_id": "Utente Matrix", + "downvotes_disabled": "Downvote disabilitati", + "post_title_too_long": "Titolo del post troppo lungo.", + "email_already_exists": "Indirizzo email già presente." } From e902ecab36d39e729d56ca370db4453744eebbe6 Mon Sep 17 00:00:00 2001 From: olivia maia Date: Fri, 6 Mar 2020 10:58:36 +0000 Subject: [PATCH 108/164] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (230 of 230 strings) Translation: Lemmy/lemmy Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/pt_BR/ --- ui/translations/pt_BR.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/translations/pt_BR.json b/ui/translations/pt_BR.json index 390fddc16..7060a09a8 100644 --- a/ui/translations/pt_BR.json +++ b/ui/translations/pt_BR.json @@ -184,7 +184,7 @@ "are_you_sure": "tem certeza?", "yes": "sim", "no": "não", - "powered_by": "Powered by", + "powered_by": "Fornecido por", "landing_0": "Lemmy é um <1>agregador de links / alternativa ao reddit, com a intenção de funcionar junto ao <2>fediverso.<3>Pode ser hospedado em servidor próprio, tem atualização de comentários em tempo real e é minúsculo (<4>~80kB). A federação com a rede ActivityPub está no roteiro do projeto. <5>Esta é uma <6>versão beta bastante antecipada, e muitas funcionalidades ainda estão quebradas ou ausentes. <7>Sugira novas funcionalidades ou reporte erros <8>aqui.<9>Feito com <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.", "not_logged_in": "Não autenticado.", "logged_in": "Autenticado.", @@ -193,7 +193,7 @@ "couldnt_create_comment": "Não foi possível criar o comentário.", "couldnt_like_comment": "Não foi possível curtir o comentário.", "couldnt_update_comment": "Não foi possível atualizar o comentário.", - "couldnt_save_comment": "Não foi possível guardar o comentário.", + "couldnt_save_comment": "Não foi possível salvar o comentário.", "no_comment_edit_allowed": "Sem permissão para editar comentário.", "no_post_edit_allowed": "Sem permissão para editar publicação.", "no_community_edit_allowed": "Sem permissão para editar comunidade.", @@ -227,6 +227,6 @@ "time": "Tempo", "action": "Ação", "more": "mais", - "couldnt_get_comments": "Não foi possível receber os comentários.", + "couldnt_get_comments": "Não foi possível obter os comentários.", "post_title_too_long": "Título da publicação muito longo." } From b9ab38bdab5ea2f261965f7ba9a44ed45247270a Mon Sep 17 00:00:00 2001 From: ButterflyOfFire Date: Fri, 6 Mar 2020 10:58:36 +0000 Subject: [PATCH 109/164] Translated using Weblate (Arabic) Currently translated at 77.3% (178 of 230 strings) Translation: Lemmy/lemmy Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/ar/ --- ui/translations/ar.json | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/ui/translations/ar.json b/ui/translations/ar.json index 91a21d111..44ab84f55 100644 --- a/ui/translations/ar.json +++ b/ui/translations/ar.json @@ -140,5 +140,41 @@ "time": "الساعة", "more": "المزيد", "community_ban": "لقد تم طردك مِن هذا المجتمع.", - "action": "الإجراء" + "action": "الإجراء", + "expires": "تنتهي صلاحيته في", + "support_on_patreon": "ساندنا على Patreon", + "support_on_liberapay": "ساندنا عبر Liberapay", + "crypto": "العملات الرقمية", + "number_of_comments": "{{count}} تعليقات", + "cross_posts": "لقد تم نشر هذا الرابط كذلك على:", + "cross_post": "منشور نُشِر تبادليا", + "cross_posted_to": "نشر تبادلي إلى: ", + "trending_communities": "<1>المجتمعات الشائعة", + "unlock": "إلغاء الحظر", + "lock": "حظر", + "archive_link": "أرشفة الرابط", + "locked": "محظور", + "unban": "إلغاء الطرد", + "unban_from_site": "إلغاء الطرد مِن الموقع", + "unsave": "إزالة", + "top": "المتداولة", + "api": "API", + "inbox": "صندوق الوارد", + "mark_all_as_read": "تعيين الكل كمقروء", + "unread": "غير مقروء", + "unread_messages": "الرسائل غير المقروءة", + "password_change": "تعديل الكلمة السرية", + "no_email_setup": "لم يتم إعداد البريد الإلكتروني في هذا الخادم بشكل جيد.", + "send_notifications_to_email": "إرسال الإشعارات عبر البريد الإلكتروني", + "browser_default": "افتراضي للمتصفح", + "downvotes_disabled": "تم تعطيل التصويتات السلبية", + "enable_downvotes": "تم تفعيل التصويتات الإيجابية", + "open_registration": "السماح بإنشاء الحسابات", + "registration_closed": "إنشاء الحسابات معطل", + "enable_nsfw": "تنشيط المحتوى الحساس", + "expand_here": "وسّعه مِن هنا", + "lemmy_instance_setup": "تنصيب مثيل خادم Lemmy", + "show_nsfw": "إظهار المحتوى الحساس", + "sponsors": "الرعاة", + "sponsors_of_lemmy": "رعاة مشروع Lemmy" } From ab2012d12c433dc752ea5ad935942e1fb992f112 Mon Sep 17 00:00:00 2001 From: Policarp Date: Fri, 6 Mar 2020 10:58:36 +0000 Subject: [PATCH 110/164] Translated using Weblate (Russian) Currently translated at 87.3% (201 of 230 strings) Translation: Lemmy/lemmy Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/ru/ --- ui/translations/ru.json | 60 ++++++++++++++++++++++++++++++++--------- 1 file changed, 48 insertions(+), 12 deletions(-) diff --git a/ui/translations/ru.json b/ui/translations/ru.json index 4d708173e..071066edf 100644 --- a/ui/translations/ru.json +++ b/ui/translations/ru.json @@ -92,8 +92,7 @@ "login_sign_up": "Войти / Регистрация", "login": "Авторизация", "sign_up": "Регистрация", - "notifications_error": - "Уведомления на рабочем столе недоступны в вашем браузере. Попробуйте Firefox или Chrome.", + "notifications_error": "Уведомления на рабочем столе недоступны в вашем браузере. Попробуйте Firefox или Chrome.", "unread_messages": "Непрочитанные сообщения", "password": "Пароль", "verify_password": "Повторите пароль", @@ -117,19 +116,16 @@ "show_nsfw": "Показывать NSFW-контент", "sponsors": "Спонсоры", "sponsors_of_lemmy": "Спонсоры Lemmy", - "sponsor_message": - "Lemmy это бесплатное, <1>открытое программное обеспечение, что означает отсутствие рекламы, монетизации или венчурного капитала, никогда. Ваши пожертвования напрямую поддерживают развитие проекта. Спасибо нижеуказанным людям:", + "sponsor_message": "Lemmy это бесплатное, <1>открытое программное обеспечение, что означает отсутствие рекламы, монетизации или венчурного капитала, никогда. Ваши пожертвования напрямую поддерживают развитие проекта. Спасибо нижеуказанным людям:", "support_on_patreon": "Поддержать на Patreon", - "general_sponsors": - "Генеральные спонсоры - это те, кто пообещал Lemmy от $10 до $39.", + "general_sponsors": "Генеральные спонсоры - это те, кто пообещал Lemmy от $10 до $39.", "crypto": "Крипто", "bitcoin": "Bitcoin", "ethereum": "Ethereum", "code": "Код", "joined": "Присоединился", "powered_by": "Работает на", - "landing_0": - "Lemmy - это <1>агрегатор ссылок / альтернатива reddit, предназначенный для работы в <2>федиверсе.<3>Это самодостаточная система, с обновляемыми комментариями, и эта система крошечная (<4>~80 Кб). Федерация в сети ActivityPub находится в разработке. <5>Это <6>очень ранняя бета-версия, и многие функции в настоящее время сломаны или отсутствуют. <7>Предлагать новые функции или сообщать об ошибках можно <8>здесь.<9>Сделано на <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.", + "landing_0": "Lemmy - это <1>агрегатор ссылок / альтернатива reddit, предназначенный для работы в <2>федиверсе.<3>Это самодостаточная система, с обновляемыми комментариями, и эта система крошечная (<4>~80 Кб). Федерация в сети ActivityPub находится в разработке. <5>Это <6>очень ранняя бета-версия, и многие функции в настоящее время сломаны или отсутствуют. <7>Предлагать новые функции или сообщать об ошибках можно <8>здесь.<9>Сделано на <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.", "not_logged_in": "Не авторизованы.", "community_ban": "Вы были заблокированы на данном сообществе.", "site_ban": "Вы были заблокированы на данном сайте", @@ -156,13 +152,53 @@ "not_an_admin": "Не администратор.", "site_already_exists": "Сайт уже существует.", "couldnt_update_site": "Не получилось обновить сайт.", - "couldnt_find_that_username_or_email": - "Не получилось найти данное имя пользователя или электронную почту.", + "couldnt_find_that_username_or_email": "Не получилось найти данное имя пользователя или электронную почту.", "password_incorrect": "Неверный пароль.", "passwords_dont_match": "Пароли не совпадают.", "admin_already_created": "Извините, уже есть администратор.", "user_already_exists": "Пользователь уже существует.", "couldnt_update_user": "Не получилось обновить пользователя.", - "system_err_login": - "Системная ошибка. Попробуйте выйти из системы и вернуться обратно." + "system_err_login": "Системная ошибка. Попробуйте выйти из системы и вернуться обратно.", + "create_private_message": "Создать личное сообщение", + "send_secure_message": "Послать зашифрованное сообщение", + "send_message": "Послать сообщение", + "message": "Сообщение", + "avatar": "Аватар", + "show_avatars": "Показать Аватары", + "formatting_help": "Помощь в верстке текста", + "sticky": "", + "stickied": "закрепленный пост", + "delete_account": "Удалить аккаунт", + "delete_account_confirm": "Предупреждение: это действите полностью уничтожит все данные вашего аккаунта. Введите свой пароль для подтверждения.", + "docs": "Документация", + "replies": "Ответы", + "mentions": "Упоминания", + "message_sent": "Сообщение отправлено", + "old_password": "Действующий пароль", + "forgot_password": "я забыл(а) пароль", + "reset_password_mail_sent": "Имейл для восстановления пароля был выслан.", + "private_message_disclaimer": "Предупреждение: Приватные сообщения Lemmy на данный момент не зашифрованы. Для безопасной коммуникации создайте аккаунт на <1>Riot.im.", + "send_notifications_to_email": "Посылать уведомления на e-mail адрес", + "language": "Язык", + "browser_default": "Браузер по умолчанию", + "open_registration": "Открыть регистрацию", + "registration_closed": "Регистрация закрыта", + "recent_comments": "Недавние комментарии", + "cross_posts": "Эта ссылка была также опубликована в следующих сообществах:", + "cross_post": "Опубликовать в других сообществах", + "cross_posted_to": "Также опубликовано в: ", + "support_on_liberapay": "Поддержать на Librepay", + "donate_to_lemmy": "Поддержать Lemmy", + "transfer_community": "передать сообщество", + "yes": "да", + "no": "нет", + "preview": "Предварительный просмотр", + "upload_image": "загрузить изображение", + "upload_avatar": "Загрузить Аватар", + "messages": "Сообщения", + "new_password": "Новый пароль", + "theme": "Визуальная тема", + "post_title_too_long": "Длина названия поста превышает допустимый лимит.", + "time": "Время", + "action": "Действие" } From 08af66ba28f38339a5ae655493d3ab299508d910 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Fri, 6 Mar 2020 09:55:32 -0500 Subject: [PATCH 111/164] Some comment-node additions - Hiding extra vote counts if no downvotes. - Showing numbers on actions if there are. --- ui/assets/css/main.css | 2 +- ui/src/components/comment-node.tsx | 90 ++++++++++++++---------------- ui/src/components/post.tsx | 2 +- ui/src/utils.ts | 15 ++++- 4 files changed, 58 insertions(+), 51 deletions(-) diff --git a/ui/assets/css/main.css b/ui/assets/css/main.css index 53237793f..b458a9d20 100644 --- a/ui/assets/css/main.css +++ b/ui/assets/css/main.css @@ -117,7 +117,7 @@ } blockquote { - border-left: 1px solid var(--secondary); + border-left: 2px solid var(--secondary); margin: 0.5em 5px; padding: 0.1em 5px; } diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index db3c589d1..524367bce 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -125,7 +125,10 @@ export class CommentNode extends Component { }`} > {!node.comment.parent_id && !this.props.noIndent && ( -
                + <> +
                +
                + )}
                { style={ !this.props.noIndent && this.props.node.comment.parent_id && - `border-left: 1px ${this.state.borderColor} solid !important` + `border-left: 2px ${this.state.borderColor} solid !important` } >
                { {i18n.t('banned')}
              • )} -
              • - -
              • - - - - - {this.state.score} - -
              • -
              • - - - - {this.state.upvotes} -
              • -
              • - - - - {this.state.downvotes} -
              • -
                {this.props.showCommunity && (
              • {i18n.t('to')} @@ -219,6 +194,21 @@ export class CommentNode extends Component {
              • )}
              • +
              • + + + + + {this.state.score} + +
              • +
              • @@ -230,11 +220,11 @@ export class CommentNode extends Component { onClick={linkEvent(this, this.handleCommentCollapse)} > {this.state.collapsed ? ( - + ) : ( - + )} @@ -292,9 +282,12 @@ export class CommentNode extends Component { onClick={linkEvent(node, this.handleCommentUpvote)} data-tippy-content={i18n.t('upvote')} > - + + {this.state.upvotes !== this.state.score && ( + {this.state.upvotes} + )}
              • {WebSocketService.Instance.site.enable_downvotes && ( @@ -311,9 +304,12 @@ export class CommentNode extends Component { )} data-tippy-content={i18n.t('downvote')} > - + + {this.state.upvotes !== this.state.score && ( + {this.state.downvotes} + )} )} @@ -328,19 +324,6 @@ export class CommentNode extends Component { - {!this.myComment && ( -
              • - - - - - -
              • - )}
              • {
              • ) : ( <> + {!this.myComment && ( +
              • + + + + + +
              • + )}
              • { sortRadios() { return ( -
                +
              • +
              • -
              • - - - - - {this.state.upvotes} - -
              • -
              • - - - - - {this.state.downvotes} - -
              • +
              • + {this.state.upvotes !== this.state.score && ( + <> +
              • + + + + + {this.state.upvotes} + +
              • +
              • + + + + + {this.state.downvotes} + +
              • +
              • + + )}
              • Date: Fri, 6 Mar 2020 12:48:51 -0500 Subject: [PATCH 113/164] Reordering activitypub resources. --- docs/src/about_goals.md | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/docs/src/about_goals.md b/docs/src/about_goals.md index d8a71794d..1463eeb07 100644 --- a/docs/src/about_goals.md +++ b/docs/src/about_goals.md @@ -1,4 +1,5 @@ # Goals + - Come up with a name / codename. - Must have communities. - Must have threaded comments. @@ -7,6 +8,7 @@ - Use websockets for post / gets to your own instance. # Questions + - How does voting work? Should we go back to the old way of showing up and downvote counts? Or just a score? - Decide on tech to be used - Backend: Actix, Diesel. @@ -17,10 +19,7 @@ - On mobile, allow you to switch between them. Default? # Resources / Potential Libraries -- Use the [activitypub crate.](https://docs.rs/activitypub/0.1.4/activitypub/) -- https://docs.rs/activitypub/0.1.4/activitypub/ -- [Activitypub vocab.](https://www.w3.org/TR/activitystreams-vocabulary/) -- [Activitypub main](https://www.w3.org/TR/activitypub/) + - [Diesel to Postgres data types](https://kotiri.com/2018/01/31/postgresql-diesel-rust-types.html) - [helpful diesel examples](http://siciarz.net/24-days-rust-diesel/) - [Recursive query for adjacency list for nested comments](https://stackoverflow.com/questions/192220/what-is-the-most-efficient-elegant-way-to-parse-a-flat-table-into-a-tree/192462#192462) @@ -36,9 +35,15 @@ - [Temp Icon](https://www.flaticon.com/free-icon/mouse_194242) - [Rust docker build](https://shaneutt.com/blog/rust-fast-small-docker-image-builds/) - [Zurb mentions](https://github.com/zurb/tribute) -- Activitypub guides - - https://blog.joinmastodon.org/2018/06/how-to-implement-a-basic-activitypub-server/ - - https://raw.githubusercontent.com/w3c/activitypub/gh-pages/activitypub-tutorial.txt - - https://github.com/tOkeshu/activitypub-example - - https://blog.joinmastodon.org/2018/07/how-to-make-friends-and-verify-requests/ +- [TippyJS](https://github.com/atomiks/tippyjs) +## Activitypub guides + +- https://blog.joinmastodon.org/2018/06/how-to-implement-a-basic-activitypub-server/ +- https://raw.githubusercontent.com/w3c/activitypub/gh-pages/activitypub-tutorial.txt +- https://github.com/tOkeshu/activitypub-example +- https://blog.joinmastodon.org/2018/07/how-to-make-friends-and-verify-requests/ +- Use the [activitypub crate.](https://docs.rs/activitypub/0.1.4/activitypub/) +- https://docs.rs/activitypub/0.1.4/activitypub/ +- [Activitypub vocab.](https://www.w3.org/TR/activitystreams-vocabulary/) +- [Activitypub main](https://www.w3.org/TR/activitypub/) From 970e0b5185de35bb7f27e7ee7a8f4efb949e5a1b Mon Sep 17 00:00:00 2001 From: Dessalines Date: Fri, 6 Mar 2020 13:24:12 -0500 Subject: [PATCH 114/164] Version v0.6.30 --- ansible/VERSION | 2 +- docker/prod/docker-compose.yml | 2 +- server/src/version.rs | 2 +- ui/src/version.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ansible/VERSION b/ansible/VERSION index 49d70ca7e..a5508fb44 100644 --- a/ansible/VERSION +++ b/ansible/VERSION @@ -1 +1 @@ -v0.6.29 +v0.6.30 diff --git a/docker/prod/docker-compose.yml b/docker/prod/docker-compose.yml index 53de5d088..8ce4d5835 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.29 + image: dessalines/lemmy:v0.6.30 ports: - "127.0.0.1:8536:8536" restart: always diff --git a/server/src/version.rs b/server/src/version.rs index 2c69014cb..0744c209d 100644 --- a/server/src/version.rs +++ b/server/src/version.rs @@ -1 +1 @@ -pub const VERSION: &str = "v0.6.29"; +pub const VERSION: &str = "v0.6.30"; diff --git a/ui/src/version.ts b/ui/src/version.ts index 64c6affae..d6ef820f7 100644 --- a/ui/src/version.ts +++ b/ui/src/version.ts @@ -1 +1 @@ -export const version: string = 'v0.6.29'; +export const version: string = 'v0.6.30'; From 0708a6d665ef81700ac61c32f1c3710db9493108 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Fri, 6 Mar 2020 14:18:40 -0500 Subject: [PATCH 115/164] Adding a sorting help. Fixes #532 --- docs/src/about_guide.md | 12 ++++++++ docs/src/about_ranking.md | 2 +- ui/src/components/sort-select.tsx | 47 ++++++++++++++++++++----------- ui/src/utils.ts | 4 ++- ui/translations/en.json | 1 + 5 files changed, 47 insertions(+), 19 deletions(-) diff --git a/docs/src/about_guide.md b/docs/src/about_guide.md index f22e201be..16788ab93 100644 --- a/docs/src/about_guide.md +++ b/docs/src/about_guide.md @@ -6,6 +6,18 @@ Start typing... - `#a_community` to get a list of communities. - `:emoji` to get a list of emojis. +## Sorting + +*Applies to both posts and comments* + +Type | Description +--- | --- +Hot | Shows *trending* posts, based on the score, and the most recent comment time. +New | Newest posts. +Top | Shows the highest scoring posts in the given time frame. + +For more detail, check the [Post and Comment Ranking details](about_ranking.md). + ## Markdown Guide Type | Or | … to Get diff --git a/docs/src/about_ranking.md b/docs/src/about_ranking.md index 361dc24d8..d318ae82d 100644 --- a/docs/src/about_ranking.md +++ b/docs/src/about_ranking.md @@ -18,7 +18,7 @@ Score = Upvotes - Downvotes Time = time since submission (in hours) Gravity = Decay gravity, 1.8 is default ``` - +- For posts, in order to bring up active posts, it uses the latest comment time (limited to a max creation age of a month ago) - Use Max(1, score) to make sure all comments are affected by time decay. - Add 3 to the score, so that everything that has less than 3 downvotes will seem new. Otherwise all new comments would stay at zero, near the bottom. - The sign and abs of the score are necessary for dealing with the log of negative scores. diff --git a/ui/src/components/sort-select.tsx b/ui/src/components/sort-select.tsx index 5515f746c..a6ce2ea98 100644 --- a/ui/src/components/sort-select.tsx +++ b/ui/src/components/sort-select.tsx @@ -1,5 +1,6 @@ import { Component, linkEvent } from 'inferno'; import { SortType } from '../interfaces'; +import { sortingHelpUrl } from '../utils'; import { i18n } from '../i18next'; interface SortSelectProps { @@ -24,23 +25,35 @@ export class SortSelect extends Component { render() { return ( - + <> + + + + + + + ); } diff --git a/ui/src/utils.ts b/ui/src/utils.ts index 8e456cc56..ea09cf963 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -44,7 +44,9 @@ import Toastify from 'toastify-js'; import tippy from 'tippy.js'; export const repoUrl = 'https://github.com/dessalines/lemmy'; -export const markdownHelpUrl = '/docs/about_guide.html'; +export const helpGuideUrl = '/docs/about_guide.html'; +export const markdownHelpUrl = `${helpGuideUrl}#markdown-guide`; +export const sortingHelpUrl = `${helpGuideUrl}#sorting`; export const archiveUrl = 'https://archive.is'; export const postRefetchSeconds: number = 60 * 1000; diff --git a/ui/translations/en.json b/ui/translations/en.json index b097b3f1b..ea063fcaf 100644 --- a/ui/translations/en.json +++ b/ui/translations/en.json @@ -40,6 +40,7 @@ "upload_avatar": "Upload Avatar", "show_avatars": "Show Avatars", "formatting_help": "formatting help", + "sorting_help": "sorting help", "view_source": "view source", "unlock": "unlock", "lock": "lock", From a67a69f95e268a679a6c42722240628019355790 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Fri, 6 Mar 2020 14:57:52 -0500 Subject: [PATCH 116/164] Ask for confirmation on leaving pages with incomplete forms. Fixes #529 --- ui/src/components/comment-form.tsx | 5 + ui/src/components/community-form.tsx | 230 +++++++++++---------- ui/src/components/post-form.tsx | 10 + ui/src/components/private-message-form.tsx | 5 + ui/src/components/site-form.tsx | 230 +++++++++++---------- ui/translations/en.json | 3 +- 6 files changed, 266 insertions(+), 217 deletions(-) diff --git a/ui/src/components/comment-form.tsx b/ui/src/components/comment-form.tsx index aa8e651de..f3009d341 100644 --- a/ui/src/components/comment-form.tsx +++ b/ui/src/components/comment-form.tsx @@ -1,4 +1,5 @@ import { Component, linkEvent } from 'inferno'; +import { Prompt } from 'inferno-router'; import { CommentNode as CommentNodeI, CommentForm as CommentFormI, @@ -87,6 +88,10 @@ export class CommentForm extends Component { render() { return (
                +
                diff --git a/ui/src/components/community-form.tsx b/ui/src/components/community-form.tsx index aaa3e6c41..eedc20038 100644 --- a/ui/src/components/community-form.tsx +++ b/ui/src/components/community-form.tsx @@ -1,4 +1,5 @@ import { Component, linkEvent } from 'inferno'; +import { Prompt } from 'inferno-router'; import { Subscription } from 'rxjs'; import { retryWhen, delay, take } from 'rxjs/operators'; import { @@ -105,120 +106,131 @@ export class CommunityForm extends Component< render() { return ( - -
                - -
                - -
                -
                - -
                - -
                - -
                -
                -
                - -
                -