Merge branch 'dev' into federation
This commit is contained in:
commit
1336b4ed60
55 changed files with 1370 additions and 413 deletions
2
CODE_OF_CONDUCT.md
vendored
2
CODE_OF_CONDUCT.md
vendored
|
@ -30,6 +30,6 @@ In the Lemmy community we strive to go the extra step to look out for each other
|
||||||
|
|
||||||
And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good there was something you could’ve communicated better — remember that it’s your responsibility to make others comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their trust.
|
And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good there was something you could’ve communicated better — remember that it’s your responsibility to make others comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their trust.
|
||||||
|
|
||||||
The enforcement policies listed above apply to all official Lemmy venues; including git repositories under [github.com/dessalines/lemmy](https://github.com/dessalines/lemmy) and [yerbamate.dev/dessalines/lemmy](https://yerbamate.dev/dessalines/lemmy), the [Matrix channel](https://matrix.to/#/!BZVTUuEiNmRcbFeLeI:matrix.org?via=matrix.org&via=privacytools.io&via=permaweb.io); and all instances under lemmy.ml. For other projects adopting the Rust Code of Conduct, please contact the maintainers of those projects for enforcement. If you wish to use this code of conduct for your own project, consider explicitly mentioning your moderation policy or making a copy with your own moderation policy so as to avoid confusion.
|
The enforcement policies listed above apply to all official Lemmy venues; including git repositories under [github.com/LemmyNet/lemmy](https://github.com/LemmyNet/lemmy) and [yerbamate.dev/dessalines/lemmy](https://yerbamate.dev/dessalines/lemmy), the [Matrix channel](https://matrix.to/#/!BZVTUuEiNmRcbFeLeI:matrix.org?via=matrix.org&via=privacytools.io&via=permaweb.io); and all instances under lemmy.ml. For other projects adopting the Rust Code of Conduct, please contact the maintainers of those projects for enforcement. If you wish to use this code of conduct for your own project, consider explicitly mentioning your moderation policy or making a copy with your own moderation policy so as to avoid confusion.
|
||||||
|
|
||||||
Adapted from the [Rust Code of Conduct](https://www.rust-lang.org/policies/code-of-conduct), which is based on the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) as well as the [Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).
|
Adapted from the [Rust Code of Conduct](https://www.rust-lang.org/policies/code-of-conduct), which is based on the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) as well as the [Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).
|
||||||
|
|
23
README.md
vendored
23
README.md
vendored
|
@ -1,12 +1,12 @@
|
||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||
![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/dessalines/lemmy.svg)
|
![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/LemmyNet/lemmy.svg)
|
||||||
[![Build Status](https://travis-ci.org/dessalines/lemmy.svg?branch=master)](https://travis-ci.org/dessalines/lemmy)
|
[![Build Status](https://travis-ci.org/LemmyNet/lemmy.svg?branch=master)](https://travis-ci.org/LemmyNet/lemmy)
|
||||||
[![GitHub issues](https://img.shields.io/github/issues-raw/dessalines/lemmy.svg)](https://github.com/dessalines/lemmy/issues)
|
[![GitHub issues](https://img.shields.io/github/issues-raw/LemmyNet/lemmy.svg)](https://github.com/LemmyNet/lemmy/issues)
|
||||||
[![Docker Pulls](https://img.shields.io/docker/pulls/dessalines/lemmy.svg)](https://cloud.docker.com/repository/docker/dessalines/lemmy/)
|
[![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/)
|
[![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)
|
[![License](https://img.shields.io/github/license/LemmyNet/lemmy.svg)](LICENSE)
|
||||||
![GitHub stars](https://img.shields.io/github/stars/dessalines/lemmy?style=social)
|
![GitHub stars](https://img.shields.io/github/stars/LemmyNet/lemmy?style=social)
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
|
@ -22,11 +22,11 @@
|
||||||
·
|
·
|
||||||
<a href="https://dev.lemmy.ml/docs/index.html">Documentation</a>
|
<a href="https://dev.lemmy.ml/docs/index.html">Documentation</a>
|
||||||
·
|
·
|
||||||
<a href="https://github.com/dessalines/lemmy/issues">Report Bug</a>
|
<a href="https://github.com/LemmyNet/lemmy/issues">Report Bug</a>
|
||||||
·
|
·
|
||||||
<a href="https://github.com/dessalines/lemmy/issues">Request Feature</a>
|
<a href="https://github.com/LemmyNet/lemmy/issues">Request Feature</a>
|
||||||
·
|
·
|
||||||
<a href="https://github.com/dessalines/lemmy/blob/master/RELEASES.md">Releases</a>
|
<a href="https://github.com/LemmyNet/lemmy/blob/master/RELEASES.md">Releases</a>
|
||||||
</p>
|
</p>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ Front Page|Post
|
||||||
---|---
|
---|---
|
||||||
![main screen](https://i.imgur.com/kZSRcRu.png)|![chat screen](https://i.imgur.com/4XghNh6.png)
|
![main screen](https://i.imgur.com/kZSRcRu.png)|![chat screen](https://i.imgur.com/4XghNh6.png)
|
||||||
|
|
||||||
[Lemmy](https://github.com/dessalines/lemmy) is similar to sites like [Reddit](https://reddit.com), [Lobste.rs](https://lobste.rs), [Raddle](https://raddle.me), or [Hacker News](https://news.ycombinator.com/): you subscribe to forums you're interested in, post links and discussions, then vote, and comment on them. Behind the scenes, it is very different; anyone can easily run a server, and all these servers are federated (think email), and connected to the same universe, called the [Fediverse](https://en.wikipedia.org/wiki/Fediverse).
|
[Lemmy](https://github.com/LemmyNet/lemmy) is similar to sites like [Reddit](https://reddit.com), [Lobste.rs](https://lobste.rs), [Raddle](https://raddle.me), or [Hacker News](https://news.ycombinator.com/): you subscribe to forums you're interested in, post links and discussions, then vote, and comment on them. Behind the scenes, it is very different; anyone can easily run a server, and all these servers are federated (think email), and connected to the same universe, called the [Fediverse](https://en.wikipedia.org/wiki/Fediverse).
|
||||||
|
|
||||||
For a link aggregator, this means a user registered on one server can subscribe to forums on any other server, and can have discussions with users registered elsewhere.
|
For a link aggregator, this means a user registered on one server can subscribe to forums on any other server, and can have discussions with users registered elsewhere.
|
||||||
|
|
||||||
|
@ -108,8 +108,9 @@ Each lemmy server can set its own moderation policy; appointing site-wide admins
|
||||||
|
|
||||||
Lemmy is free, open-source software, meaning no advertising, monetizing, or venture capital, ever. Your donations directly support full-time development of the project.
|
Lemmy is free, open-source software, meaning no advertising, monetizing, or venture capital, ever. Your donations directly support full-time development of the project.
|
||||||
|
|
||||||
- [Support on Liberapay.](https://liberapay.com/Lemmy)
|
- [Support on Liberapay](https://liberapay.com/Lemmy).
|
||||||
- [Support on Patreon](https://www.patreon.com/dessalines).
|
- [Support on Patreon](https://www.patreon.com/dessalines).
|
||||||
|
- [Support on OpenCollective](https://opencollective.com/lemmy).
|
||||||
- [List of Sponsors](https://dev.lemmy.ml/sponsors).
|
- [List of Sponsors](https://dev.lemmy.ml/sponsors).
|
||||||
|
|
||||||
### Crypto
|
### Crypto
|
||||||
|
@ -132,7 +133,7 @@ If you want to help with translating, take a look at [Weblate](https://weblate.y
|
||||||
|
|
||||||
- [Mastodon](https://mastodon.social/@LemmyDev) - [![Mastodon Follow](https://img.shields.io/mastodon/follow/810572?domain=https%3A%2F%2Fmastodon.social&style=social)](https://mastodon.social/@LemmyDev)
|
- [Mastodon](https://mastodon.social/@LemmyDev) - [![Mastodon Follow](https://img.shields.io/mastodon/follow/810572?domain=https%3A%2F%2Fmastodon.social&style=social)](https://mastodon.social/@LemmyDev)
|
||||||
- [Matrix](https://riot.im/app/#/room/#rust-reddit-fediverse:matrix.org) - [![Matrix](https://img.shields.io/matrix/rust-reddit-fediverse:matrix.org.svg?label=matrix-chat)](https://riot.im/app/#/room/#rust-reddit-fediverse:matrix.org)
|
- [Matrix](https://riot.im/app/#/room/#rust-reddit-fediverse:matrix.org) - [![Matrix](https://img.shields.io/matrix/rust-reddit-fediverse:matrix.org.svg?label=matrix-chat)](https://riot.im/app/#/room/#rust-reddit-fediverse:matrix.org)
|
||||||
- [GitHub](https://github.com/dessalines/lemmy)
|
- [GitHub](https://github.com/LemmyNet/lemmy)
|
||||||
- [Gitea](https://yerbamate.dev/dessalines/lemmy)
|
- [Gitea](https://yerbamate.dev/dessalines/lemmy)
|
||||||
- [GitLab](https://gitlab.com/dessalines/lemmy)
|
- [GitLab](https://gitlab.com/dessalines/lemmy)
|
||||||
|
|
||||||
|
|
4
RELEASES.md
vendored
4
RELEASES.md
vendored
|
@ -1,6 +1,6 @@
|
||||||
# Lemmy v0.6.0 Release (2020-01-16)
|
# Lemmy v0.6.0 Release (2020-01-16)
|
||||||
|
|
||||||
`v0.6.0` is here, and we've closed [41 issues!](https://github.com/dessalines/lemmy/milestone/15?closed=1)
|
`v0.6.0` is here, and we've closed [41 issues!](https://github.com/LemmyNet/lemmy/milestone/15?closed=1)
|
||||||
|
|
||||||
This is the biggest release by far:
|
This is the biggest release by far:
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ This is the biggest release by far:
|
||||||
- Can set a custom language.
|
- Can set a custom language.
|
||||||
- Lemmy-wide settings to disable downvotes, and close registration.
|
- Lemmy-wide settings to disable downvotes, and close registration.
|
||||||
- A better documentation system, hosted in lemmy itself.
|
- A better documentation system, hosted in lemmy itself.
|
||||||
- [Huge DB performance gains](https://github.com/dessalines/lemmy/issues/411) (everthing down to < `30ms`) by using materialized views.
|
- [Huge DB performance gains](https://github.com/LemmyNet/lemmy/issues/411) (everthing down to < `30ms`) by using materialized views.
|
||||||
- Fixed major issue with similar post URL and title searching.
|
- Fixed major issue with similar post URL and title searching.
|
||||||
- Upgraded to Actix `2.0`
|
- Upgraded to Actix `2.0`
|
||||||
- Faster comment / post voting.
|
- Faster comment / post voting.
|
||||||
|
|
2
ansible/VERSION
vendored
2
ansible/VERSION
vendored
|
@ -1 +1 @@
|
||||||
v0.6.44
|
v0.6.49
|
||||||
|
|
4
docker/dev/deploy.sh
vendored
4
docker/dev/deploy.sh
vendored
|
@ -72,5 +72,5 @@ git push origin $new_tag
|
||||||
git push
|
git push
|
||||||
|
|
||||||
# Pushing to any ansible deploys
|
# Pushing to any ansible deploys
|
||||||
cd ../../ansible || exit
|
cd ../../../lemmy-ansible || exit
|
||||||
ansible-playbook lemmy.yml --become
|
ansible-playbook -i prod playbooks/site.yml --vault-password-file vault_pass
|
||||||
|
|
12
docker/dev/dev_deploy.sh
vendored
12
docker/dev/dev_deploy.sh
vendored
|
@ -1,12 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# Building from the dev branch for dev servers
|
|
||||||
git checkout dev
|
|
||||||
|
|
||||||
# Rebuilding dev docker
|
|
||||||
docker-compose build
|
|
||||||
docker tag dev_lemmy:latest dessalines/lemmy:dev
|
|
||||||
docker push dessalines/lemmy:dev
|
|
||||||
|
|
||||||
# SSH and pull it
|
|
||||||
ssh $LEMMY_USER@$LEMMY_HOST "cd ~/git/lemmy/docker/dev && docker pull dessalines/lemmy:dev && docker-compose up -d"
|
|
2
docker/dev/docker-compose.yml
vendored
2
docker/dev/docker-compose.yml
vendored
|
@ -21,7 +21,7 @@ services:
|
||||||
environment:
|
environment:
|
||||||
- RUST_LOG=debug
|
- RUST_LOG=debug
|
||||||
volumes:
|
volumes:
|
||||||
- ../lemmy.hjson:/config/config.hjson:ro
|
- ../lemmy.hjson:/config/config.hjson
|
||||||
depends_on:
|
depends_on:
|
||||||
- postgres
|
- postgres
|
||||||
- pictshare
|
- pictshare
|
||||||
|
|
11
docker/dev/test_deploy.sh
vendored
Executable file
11
docker/dev/test_deploy.sh
vendored
Executable file
|
@ -0,0 +1,11 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Rebuilding dev docker
|
||||||
|
docker-compose build
|
||||||
|
docker tag dev_lemmy:latest dessalines/lemmy:test
|
||||||
|
docker push dessalines/lemmy:test
|
||||||
|
|
||||||
|
# Run the playbook
|
||||||
|
pushd ../../../lemmy-ansible
|
||||||
|
ansible-playbook -i test playbooks/site.yml --vault-password-file vault_pass
|
||||||
|
popd
|
4
docker/prod/docker-compose.yml
vendored
4
docker/prod/docker-compose.yml
vendored
|
@ -12,14 +12,14 @@ services:
|
||||||
restart: always
|
restart: always
|
||||||
|
|
||||||
lemmy:
|
lemmy:
|
||||||
image: dessalines/lemmy:v0.6.44
|
image: dessalines/lemmy:v0.6.49
|
||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:8536:8536"
|
- "127.0.0.1:8536:8536"
|
||||||
restart: always
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
- RUST_LOG=error
|
- RUST_LOG=error
|
||||||
volumes:
|
volumes:
|
||||||
- ./lemmy.hjson:/config/config.hjson:ro
|
- ./lemmy.hjson:/config/config.hjson
|
||||||
depends_on:
|
depends_on:
|
||||||
- postgres
|
- postgres
|
||||||
- pictshare
|
- pictshare
|
||||||
|
|
1
docs/src/SUMMARY.md
vendored
1
docs/src/SUMMARY.md
vendored
|
@ -10,6 +10,7 @@
|
||||||
- [Install with Ansible](administration_install_ansible.md)
|
- [Install with Ansible](administration_install_ansible.md)
|
||||||
- [Install with Kubernetes](administration_install_kubernetes.md)
|
- [Install with Kubernetes](administration_install_kubernetes.md)
|
||||||
- [Configuration](administration_configuration.md)
|
- [Configuration](administration_configuration.md)
|
||||||
|
- [Backup and Restore](administration_backup_and_restore.md)
|
||||||
- [Contributing](contributing.md)
|
- [Contributing](contributing.md)
|
||||||
- [Docker Development](contributing_docker_development.md)
|
- [Docker Development](contributing_docker_development.md)
|
||||||
- [Local Development](contributing_local_development.md)
|
- [Local Development](contributing_local_development.md)
|
||||||
|
|
2
docs/src/about.md
vendored
2
docs/src/about.md
vendored
|
@ -4,7 +4,7 @@ Front Page|Post
|
||||||
---|---
|
---|---
|
||||||
![main screen](https://i.imgur.com/kZSRcRu.png)|![chat screen](https://i.imgur.com/4XghNh6.png)
|
![main screen](https://i.imgur.com/kZSRcRu.png)|![chat screen](https://i.imgur.com/4XghNh6.png)
|
||||||
|
|
||||||
[Lemmy](https://github.com/dessalines/lemmy) is similar to sites like [Reddit](https://reddit.com), [Lobste.rs](https://lobste.rs), [Raddle](https://raddle.me), or [Hacker News](https://news.ycombinator.com/): you subscribe to forums you're interested in, post links and discussions, then vote, and comment on them. Behind the scenes, it is very different; anyone can easily run a server, and all these servers are federated (think email), and connected to the same universe, called the [Fediverse](https://en.wikipedia.org/wiki/Fediverse).
|
[Lemmy](https://github.com/LemmyNet/lemmy) is similar to sites like [Reddit](https://reddit.com), [Lobste.rs](https://lobste.rs), [Raddle](https://raddle.me), or [Hacker News](https://news.ycombinator.com/): you subscribe to forums you're interested in, post links and discussions, then vote, and comment on them. Behind the scenes, it is very different; anyone can easily run a server, and all these servers are federated (think email), and connected to the same universe, called the [Fediverse](https://en.wikipedia.org/wiki/Fediverse).
|
||||||
|
|
||||||
For a link aggregator, this means a user registered on one server can subscribe to forums on any other server, and can have discussions with users registered elsewhere.
|
For a link aggregator, this means a user registered on one server can subscribe to forums on any other server, and can have discussions with users registered elsewhere.
|
||||||
|
|
||||||
|
|
1
docs/src/about_goals.md
vendored
1
docs/src/about_goals.md
vendored
|
@ -51,3 +51,4 @@
|
||||||
- [Activitypub implementers guide](https://socialhub.activitypub.rocks/t/draft-guide-for-new-activitypub-implementers/479)
|
- [Activitypub implementers guide](https://socialhub.activitypub.rocks/t/draft-guide-for-new-activitypub-implementers/479)
|
||||||
- [Data storage questions](https://socialhub.activitypub.rocks/t/data-storage-questions/579/3)
|
- [Data storage questions](https://socialhub.activitypub.rocks/t/data-storage-questions/579/3)
|
||||||
- [Activitypub as it has been understood](https://flak.tedunangst.com/post/ActivityPub-as-it-has-been-understood)
|
- [Activitypub as it has been understood](https://flak.tedunangst.com/post/ActivityPub-as-it-has-been-understood)
|
||||||
|
- [Asonix http signatures in rust](https://git.asonix.dog/Aardwolf/http-signature-normalization)
|
||||||
|
|
44
docs/src/administration_backup_and_restore.md
vendored
Normal file
44
docs/src/administration_backup_and_restore.md
vendored
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
# Backup and Restore Guide
|
||||||
|
|
||||||
|
## Docker and Ansible
|
||||||
|
|
||||||
|
When using docker or ansible, there should be a `volumes` folder, which contains both the database, and all the pictures. Copy this folder to the new instance to restore your data.
|
||||||
|
|
||||||
|
### Incremental Database backup
|
||||||
|
|
||||||
|
To incrementally backup the DB to an `.sql` file, you can run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec -t FOLDERNAME_postgres_1 pg_dumpall -c -U lemmy > lemmy_dump_`date +%Y-%m-%d"_"%H_%M_%S`.sql
|
||||||
|
```
|
||||||
|
### A Sample backup script
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/sh
|
||||||
|
# DB Backup
|
||||||
|
ssh MY_USER@MY_IP "docker exec -t FOLDERNAME_postgres_1 pg_dumpall -c -U lemmy" > ~/BACKUP_LOCATION/INSTANCE_NAME_dump_`date +%Y-%m-%d"_"%H_%M_%S`.sql
|
||||||
|
|
||||||
|
# Volumes folder Backup
|
||||||
|
rsync -avP -zz --rsync-path="sudo rsync" MY_USER@MY_IP:/LEMMY_LOCATION/volumes ~/BACKUP_LOCATION/FOLDERNAME
|
||||||
|
```
|
||||||
|
|
||||||
|
### Restoring the DB
|
||||||
|
|
||||||
|
If you need to restore from a `pg_dumpall` file, you need to first clear out your existing database
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Drop the existing DB
|
||||||
|
docker exec -i FOLDERNAME_postgres_1 psql -U lemmy -c "DROP SCHEMA public CASCADE; CREATE SCHEMA public;"
|
||||||
|
|
||||||
|
# Restore from the .sql backup
|
||||||
|
cat db_dump.sql | docker exec -i FOLDERNAME_postgres_1 psql -U lemmy # restores the db
|
||||||
|
|
||||||
|
# This also might be necessary when doing a db import with a different password.
|
||||||
|
docker exec -i FOLDERNAME_postgres_1 psql -U lemmy -c "alter user lemmy with password 'bleh'"
|
||||||
|
```
|
||||||
|
|
||||||
|
## More resources
|
||||||
|
|
||||||
|
- https://stackoverflow.com/questions/24718706/backup-restore-a-dockerized-postgresql-database
|
||||||
|
|
||||||
|
|
2
docs/src/administration_install_ansible.md
vendored
2
docs/src/administration_install_ansible.md
vendored
|
@ -7,7 +7,7 @@ First, you need to [install Ansible on your local computer](https://docs.ansible
|
||||||
Then run the following commands on your local computer:
|
Then run the following commands on your local computer:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/dessalines/lemmy.git
|
git clone https://github.com/LemmyNet/lemmy.git
|
||||||
cd lemmy/ansible/
|
cd lemmy/ansible/
|
||||||
cp inventory.example inventory
|
cp inventory.example inventory
|
||||||
nano inventory # enter your server, domain, contact email
|
nano inventory # enter your server, domain, contact email
|
||||||
|
|
5
docs/src/contributing.md
vendored
5
docs/src/contributing.md
vendored
|
@ -4,13 +4,14 @@ Information about contributing to Lemmy, whether it is translating, testing, des
|
||||||
|
|
||||||
## Issue tracking / Repositories
|
## Issue tracking / Repositories
|
||||||
|
|
||||||
- [GitHub (for issues)](https://github.com/dessalines/lemmy)
|
- [GitHub (for issues)](https://github.com/LemmyNet/lemmy)
|
||||||
- [Gitea](https://yerbamate.dev/dessalines/lemmy)
|
- [Gitea](https://yerbamate.dev/dessalines/lemmy)
|
||||||
- [GitLab](https://gitlab.com/dessalines/lemmy)
|
- [GitLab](https://gitlab.com/dessalines/lemmy)
|
||||||
|
|
||||||
## Translating
|
## Translating
|
||||||
|
|
||||||
Go [here](https://github.com/dessalines/lemmy#translations) for translation instructions.
|
Check out [Lemmy's Weblate](https://weblate.yerbamate.dev/projects/lemmy/) for translations.
|
||||||
|
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
|
|
2
docs/src/contributing_docker_development.md
vendored
2
docs/src/contributing_docker_development.md
vendored
|
@ -3,7 +3,7 @@
|
||||||
## Running
|
## Running
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/dessalines/lemmy
|
git clone https://github.com/LemmyNet/lemmy
|
||||||
cd lemmy/docker/dev
|
cd lemmy/docker/dev
|
||||||
./docker_update.sh # This builds and runs it, updating for your changes
|
./docker_update.sh # This builds and runs it, updating for your changes
|
||||||
```
|
```
|
||||||
|
|
2
docs/src/contributing_local_development.md
vendored
2
docs/src/contributing_local_development.md
vendored
|
@ -22,7 +22,7 @@ export LEMMY_DATABASE_URL=postgres://lemmy:password@localhost:5432/lemmy
|
||||||
#### Running
|
#### Running
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/dessalines/lemmy
|
git clone https://github.com/LemmyNet/lemmy
|
||||||
cd lemmy
|
cd lemmy
|
||||||
./install.sh
|
./install.sh
|
||||||
# For live coding, where both the front and back end, automagically reload on any save, do:
|
# For live coding, where both the front and back end, automagically reload on any save, do:
|
||||||
|
|
99
docs/src/contributing_websocket_http_api.md
vendored
99
docs/src/contributing_websocket_http_api.md
vendored
|
@ -92,85 +92,93 @@
|
||||||
- [Request](#request-17)
|
- [Request](#request-17)
|
||||||
- [Response](#response-17)
|
- [Response](#response-17)
|
||||||
- [HTTP](#http-18)
|
- [HTTP](#http-18)
|
||||||
* [Community](#community)
|
+ [Get Site Config](#get-site-config)
|
||||||
+ [Get Community](#get-community)
|
|
||||||
- [Request](#request-18)
|
- [Request](#request-18)
|
||||||
- [Response](#response-18)
|
- [Response](#response-18)
|
||||||
- [HTTP](#http-19)
|
- [HTTP](#http-19)
|
||||||
+ [Create Community](#create-community)
|
+ [Save Site Config](#save-site-config)
|
||||||
- [Request](#request-19)
|
- [Request](#request-19)
|
||||||
- [Response](#response-19)
|
- [Response](#response-19)
|
||||||
- [HTTP](#http-20)
|
- [HTTP](#http-20)
|
||||||
+ [List Communities](#list-communities)
|
* [Community](#community)
|
||||||
|
+ [Get Community](#get-community)
|
||||||
- [Request](#request-20)
|
- [Request](#request-20)
|
||||||
- [Response](#response-20)
|
- [Response](#response-20)
|
||||||
- [HTTP](#http-21)
|
- [HTTP](#http-21)
|
||||||
+ [Ban from Community](#ban-from-community)
|
+ [Create Community](#create-community)
|
||||||
- [Request](#request-21)
|
- [Request](#request-21)
|
||||||
- [Response](#response-21)
|
- [Response](#response-21)
|
||||||
- [HTTP](#http-22)
|
- [HTTP](#http-22)
|
||||||
+ [Add Mod to Community](#add-mod-to-community)
|
+ [List Communities](#list-communities)
|
||||||
- [Request](#request-22)
|
- [Request](#request-22)
|
||||||
- [Response](#response-22)
|
- [Response](#response-22)
|
||||||
- [HTTP](#http-23)
|
- [HTTP](#http-23)
|
||||||
+ [Edit Community](#edit-community)
|
+ [Ban from Community](#ban-from-community)
|
||||||
- [Request](#request-23)
|
- [Request](#request-23)
|
||||||
- [Response](#response-23)
|
- [Response](#response-23)
|
||||||
- [HTTP](#http-24)
|
- [HTTP](#http-24)
|
||||||
+ [Follow Community](#follow-community)
|
+ [Add Mod to Community](#add-mod-to-community)
|
||||||
- [Request](#request-24)
|
- [Request](#request-24)
|
||||||
- [Response](#response-24)
|
- [Response](#response-24)
|
||||||
- [HTTP](#http-25)
|
- [HTTP](#http-25)
|
||||||
+ [Get Followed Communities](#get-followed-communities)
|
+ [Edit Community](#edit-community)
|
||||||
- [Request](#request-25)
|
- [Request](#request-25)
|
||||||
- [Response](#response-25)
|
- [Response](#response-25)
|
||||||
- [HTTP](#http-26)
|
- [HTTP](#http-26)
|
||||||
+ [Transfer Community](#transfer-community)
|
+ [Follow Community](#follow-community)
|
||||||
- [Request](#request-26)
|
- [Request](#request-26)
|
||||||
- [Response](#response-26)
|
- [Response](#response-26)
|
||||||
- [HTTP](#http-27)
|
- [HTTP](#http-27)
|
||||||
* [Post](#post)
|
+ [Get Followed Communities](#get-followed-communities)
|
||||||
+ [Create Post](#create-post)
|
|
||||||
- [Request](#request-27)
|
- [Request](#request-27)
|
||||||
- [Response](#response-27)
|
- [Response](#response-27)
|
||||||
- [HTTP](#http-28)
|
- [HTTP](#http-28)
|
||||||
+ [Get Post](#get-post)
|
+ [Transfer Community](#transfer-community)
|
||||||
- [Request](#request-28)
|
- [Request](#request-28)
|
||||||
- [Response](#response-28)
|
- [Response](#response-28)
|
||||||
- [HTTP](#http-29)
|
- [HTTP](#http-29)
|
||||||
+ [Get Posts](#get-posts)
|
* [Post](#post)
|
||||||
|
+ [Create Post](#create-post)
|
||||||
- [Request](#request-29)
|
- [Request](#request-29)
|
||||||
- [Response](#response-29)
|
- [Response](#response-29)
|
||||||
- [HTTP](#http-30)
|
- [HTTP](#http-30)
|
||||||
+ [Create Post Like](#create-post-like)
|
+ [Get Post](#get-post)
|
||||||
- [Request](#request-30)
|
- [Request](#request-30)
|
||||||
- [Response](#response-30)
|
- [Response](#response-30)
|
||||||
- [HTTP](#http-31)
|
- [HTTP](#http-31)
|
||||||
+ [Edit Post](#edit-post)
|
+ [Get Posts](#get-posts)
|
||||||
- [Request](#request-31)
|
- [Request](#request-31)
|
||||||
- [Response](#response-31)
|
- [Response](#response-31)
|
||||||
- [HTTP](#http-32)
|
- [HTTP](#http-32)
|
||||||
+ [Save Post](#save-post)
|
+ [Create Post Like](#create-post-like)
|
||||||
- [Request](#request-32)
|
- [Request](#request-32)
|
||||||
- [Response](#response-32)
|
- [Response](#response-32)
|
||||||
- [HTTP](#http-33)
|
- [HTTP](#http-33)
|
||||||
* [Comment](#comment)
|
+ [Edit Post](#edit-post)
|
||||||
+ [Create Comment](#create-comment)
|
|
||||||
- [Request](#request-33)
|
- [Request](#request-33)
|
||||||
- [Response](#response-33)
|
- [Response](#response-33)
|
||||||
- [HTTP](#http-34)
|
- [HTTP](#http-34)
|
||||||
+ [Edit Comment](#edit-comment)
|
+ [Save Post](#save-post)
|
||||||
- [Request](#request-34)
|
- [Request](#request-34)
|
||||||
- [Response](#response-34)
|
- [Response](#response-34)
|
||||||
- [HTTP](#http-35)
|
- [HTTP](#http-35)
|
||||||
+ [Save Comment](#save-comment)
|
* [Comment](#comment)
|
||||||
|
+ [Create Comment](#create-comment)
|
||||||
- [Request](#request-35)
|
- [Request](#request-35)
|
||||||
- [Response](#response-35)
|
- [Response](#response-35)
|
||||||
- [HTTP](#http-36)
|
- [HTTP](#http-36)
|
||||||
+ [Create Comment Like](#create-comment-like)
|
+ [Edit Comment](#edit-comment)
|
||||||
- [Request](#request-36)
|
- [Request](#request-36)
|
||||||
- [Response](#response-36)
|
- [Response](#response-36)
|
||||||
- [HTTP](#http-37)
|
- [HTTP](#http-37)
|
||||||
|
+ [Save Comment](#save-comment)
|
||||||
|
- [Request](#request-37)
|
||||||
|
- [Response](#response-37)
|
||||||
|
- [HTTP](#http-38)
|
||||||
|
+ [Create Comment Like](#create-comment-like)
|
||||||
|
- [Request](#request-38)
|
||||||
|
- [Response](#response-38)
|
||||||
|
- [HTTP](#http-39)
|
||||||
* [RSS / Atom feeds](#rss--atom-feeds)
|
* [RSS / Atom feeds](#rss--atom-feeds)
|
||||||
+ [All](#all)
|
+ [All](#all)
|
||||||
+ [Community](#community-1)
|
+ [Community](#community-1)
|
||||||
|
@ -779,6 +787,53 @@ Search types are `All, Comments, Posts, Communities, Users, Url`
|
||||||
|
|
||||||
`POST /site/transfer`
|
`POST /site/transfer`
|
||||||
|
|
||||||
|
#### Get Site Config
|
||||||
|
##### Request
|
||||||
|
```rust
|
||||||
|
{
|
||||||
|
op: "GetSiteConfig",
|
||||||
|
data: {
|
||||||
|
auth: String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
##### Response
|
||||||
|
```rust
|
||||||
|
{
|
||||||
|
op: "GetSiteConfig",
|
||||||
|
data: {
|
||||||
|
config_hjson: String,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
##### HTTP
|
||||||
|
|
||||||
|
`GET /site/config`
|
||||||
|
|
||||||
|
#### Save Site Config
|
||||||
|
##### Request
|
||||||
|
```rust
|
||||||
|
{
|
||||||
|
op: "SaveSiteConfig",
|
||||||
|
data: {
|
||||||
|
config_hjson: String,
|
||||||
|
auth: String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
##### Response
|
||||||
|
```rust
|
||||||
|
{
|
||||||
|
op: "SaveSiteConfig",
|
||||||
|
data: {
|
||||||
|
config_hjson: String,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
##### HTTP
|
||||||
|
|
||||||
|
`PUT /site/config`
|
||||||
|
|
||||||
### Community
|
### Community
|
||||||
#### Get Community
|
#### Get Community
|
||||||
##### Request
|
##### Request
|
||||||
|
|
|
@ -97,6 +97,22 @@ pub struct TransferSite {
|
||||||
auth: String,
|
auth: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct GetSiteConfig {
|
||||||
|
auth: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct GetSiteConfigResponse {
|
||||||
|
config_hjson: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct SaveSiteConfig {
|
||||||
|
config_hjson: String,
|
||||||
|
auth: String,
|
||||||
|
}
|
||||||
|
|
||||||
impl Perform<ListCategoriesResponse> for Oper<ListCategories> {
|
impl Perform<ListCategoriesResponse> for Oper<ListCategories> {
|
||||||
fn perform(&self, conn: &PgConnection) -> Result<ListCategoriesResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<ListCategoriesResponse, Error> {
|
||||||
let _data: &ListCategories = &self.data;
|
let _data: &ListCategories = &self.data;
|
||||||
|
@ -514,3 +530,57 @@ impl Perform<GetSiteResponse> for Oper<TransferSite> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Perform<GetSiteConfigResponse> for Oper<GetSiteConfig> {
|
||||||
|
fn perform(&self, conn: &PgConnection) -> Result<GetSiteConfigResponse, Error> {
|
||||||
|
let data: &GetSiteConfig = &self.data;
|
||||||
|
|
||||||
|
let claims = match Claims::decode(&data.auth) {
|
||||||
|
Ok(claims) => claims.claims,
|
||||||
|
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let user_id = claims.id;
|
||||||
|
|
||||||
|
// Only let admins read this
|
||||||
|
let admins = UserView::admins(&conn)?;
|
||||||
|
let admin_ids: Vec<i32> = admins.into_iter().map(|m| m.id).collect();
|
||||||
|
|
||||||
|
if !admin_ids.contains(&user_id) {
|
||||||
|
return Err(APIError::err("not_an_admin").into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let config_hjson = Settings::read_config_file()?;
|
||||||
|
|
||||||
|
Ok(GetSiteConfigResponse { config_hjson })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Perform<GetSiteConfigResponse> for Oper<SaveSiteConfig> {
|
||||||
|
fn perform(&self, conn: &PgConnection) -> Result<GetSiteConfigResponse, Error> {
|
||||||
|
let data: &SaveSiteConfig = &self.data;
|
||||||
|
|
||||||
|
let claims = match Claims::decode(&data.auth) {
|
||||||
|
Ok(claims) => claims.claims,
|
||||||
|
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let user_id = claims.id;
|
||||||
|
|
||||||
|
// Only let admins read this
|
||||||
|
let admins = UserView::admins(&conn)?;
|
||||||
|
let admin_ids: Vec<i32> = admins.into_iter().map(|m| m.id).collect();
|
||||||
|
|
||||||
|
if !admin_ids.contains(&user_id) {
|
||||||
|
return Err(APIError::err("not_an_admin").into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure docker doesn't have :ro at the end of the volume, so its not a read-only filesystem
|
||||||
|
let config_hjson = match Settings::save_config_file(&data.config_hjson) {
|
||||||
|
Ok(config_hjson) => config_hjson,
|
||||||
|
Err(_e) => return Err(APIError::err("couldnt_update_site").into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(GetSiteConfigResponse { config_hjson })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -115,7 +115,7 @@ pub fn send_email(
|
||||||
to_username: &str,
|
to_username: &str,
|
||||||
html: &str,
|
html: &str,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let email_config = Settings::get().email.as_ref().ok_or("no_email_setup")?;
|
let email_config = Settings::get().email.ok_or("no_email_setup")?;
|
||||||
|
|
||||||
let email = Email::builder()
|
let email = Email::builder()
|
||||||
.to((to_email, to_username))
|
.to((to_email, to_username))
|
||||||
|
@ -130,7 +130,7 @@ pub fn send_email(
|
||||||
} else {
|
} else {
|
||||||
SmtpClient::new(&email_config.smtp_server, ClientSecurity::None).unwrap()
|
SmtpClient::new(&email_config.smtp_server, ClientSecurity::None).unwrap()
|
||||||
}
|
}
|
||||||
.hello_name(ClientId::Domain(Settings::get().hostname.to_owned()))
|
.hello_name(ClientId::Domain(Settings::get().hostname))
|
||||||
.smtp_utf8(true)
|
.smtp_utf8(true)
|
||||||
.authentication_mechanism(Mechanism::Plain)
|
.authentication_mechanism(Mechanism::Plain)
|
||||||
.connection_reuse(ConnectionReuseParameters::ReuseUnlimited);
|
.connection_reuse(ConnectionReuseParameters::ReuseUnlimited);
|
||||||
|
|
|
@ -57,6 +57,7 @@ async fn main() -> Result<(), Error> {
|
||||||
// Create Http server with websocket support
|
// Create Http server with websocket support
|
||||||
Ok(
|
Ok(
|
||||||
HttpServer::new(move || {
|
HttpServer::new(move || {
|
||||||
|
let settings = Settings::get();
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(middleware::Logger::default())
|
.wrap(middleware::Logger::default())
|
||||||
.data(pool.clone())
|
.data(pool.clone())
|
||||||
|
|
|
@ -52,6 +52,8 @@ pub fn config(cfg: &mut web::ServiceConfig) {
|
||||||
.route("/api/v1/site", web::post().to(route_post::<CreateSite, SiteResponse>))
|
.route("/api/v1/site", web::post().to(route_post::<CreateSite, SiteResponse>))
|
||||||
.route("/api/v1/site", web::put().to(route_post::<EditSite, SiteResponse>))
|
.route("/api/v1/site", web::put().to(route_post::<EditSite, SiteResponse>))
|
||||||
.route("/api/v1/site/transfer", web::post().to(route_post::<TransferSite, GetSiteResponse>))
|
.route("/api/v1/site/transfer", web::post().to(route_post::<TransferSite, GetSiteResponse>))
|
||||||
|
.route("/api/v1/site/config", web::get().to(route_get::<GetSiteConfig, GetSiteConfigResponse>))
|
||||||
|
.route("/api/v1/site/config", web::put().to(route_post::<SaveSiteConfig, GetSiteConfigResponse>))
|
||||||
.route("/api/v1/admin/add", web::post().to(route_post::<AddAdmin, AddAdminResponse>))
|
.route("/api/v1/admin/add", web::post().to(route_post::<AddAdmin, AddAdminResponse>))
|
||||||
.route("/api/v1/user/ban", web::post().to(route_post::<BanUser, BanUserResponse>))
|
.route("/api/v1/user/ban", web::post().to(route_post::<BanUser, BanUserResponse>))
|
||||||
// User account actions
|
// User account actions
|
||||||
|
|
|
@ -33,6 +33,7 @@ pub fn config(cfg: &mut web::ServiceConfig) {
|
||||||
.route("/modlog/community/{community_id}", web::get().to(index))
|
.route("/modlog/community/{community_id}", web::get().to(index))
|
||||||
.route("/modlog", web::get().to(index))
|
.route("/modlog", web::get().to(index))
|
||||||
.route("/setup", web::get().to(index))
|
.route("/setup", web::get().to(index))
|
||||||
|
.route("/admin", web::get().to(index))
|
||||||
.route(
|
.route(
|
||||||
"/search/q/{q}/type/{type}/sort/{sort}/page/{page}",
|
"/search/q/{q}/type/{type}/sort/{sort}/page/{page}",
|
||||||
web::get().to(index),
|
web::get().to(index),
|
||||||
|
@ -44,6 +45,6 @@ pub fn config(cfg: &mut web::ServiceConfig) {
|
||||||
|
|
||||||
async fn index() -> Result<NamedFile, actix_web::error::Error> {
|
async fn index() -> Result<NamedFile, actix_web::error::Error> {
|
||||||
Ok(NamedFile::open(
|
Ok(NamedFile::open(
|
||||||
Settings::get().front_end_dir.to_owned() + "/index.html",
|
Settings::get().front_end_dir + "/index.html",
|
||||||
)?)
|
)?)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
use config::{Config, ConfigError, Environment, File};
|
use config::{Config, ConfigError, Environment, File};
|
||||||
|
use failure::Error;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
use std::fs;
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
|
use std::sync::RwLock;
|
||||||
|
|
||||||
static CONFIG_FILE_DEFAULTS: &str = "config/defaults.hjson";
|
static CONFIG_FILE_DEFAULTS: &str = "config/defaults.hjson";
|
||||||
static CONFIG_FILE: &str = "config/config.hjson";
|
static CONFIG_FILE: &str = "config/config.hjson";
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub setup: Option<Setup>,
|
pub setup: Option<Setup>,
|
||||||
pub database: Database,
|
pub database: Database,
|
||||||
|
@ -20,7 +23,7 @@ pub struct Settings {
|
||||||
pub federation: Federation,
|
pub federation: Federation,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
pub struct Setup {
|
pub struct Setup {
|
||||||
pub admin_username: String,
|
pub admin_username: String,
|
||||||
pub admin_password: String,
|
pub admin_password: String,
|
||||||
|
@ -28,7 +31,7 @@ pub struct Setup {
|
||||||
pub site_name: String,
|
pub site_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
pub struct RateLimitConfig {
|
pub struct RateLimitConfig {
|
||||||
pub message: i32,
|
pub message: i32,
|
||||||
pub message_per_second: i32,
|
pub message_per_second: i32,
|
||||||
|
@ -38,7 +41,7 @@ pub struct RateLimitConfig {
|
||||||
pub register_per_second: i32,
|
pub register_per_second: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
pub struct EmailConfig {
|
pub struct EmailConfig {
|
||||||
pub smtp_server: String,
|
pub smtp_server: String,
|
||||||
pub smtp_login: Option<String>,
|
pub smtp_login: Option<String>,
|
||||||
|
@ -47,7 +50,7 @@ pub struct EmailConfig {
|
||||||
pub use_tls: bool,
|
pub use_tls: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
pub struct Database {
|
pub struct Database {
|
||||||
pub user: String,
|
pub user: String,
|
||||||
pub password: String,
|
pub password: String,
|
||||||
|
@ -65,12 +68,10 @@ pub struct Federation {
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref SETTINGS: Settings = {
|
static ref SETTINGS: RwLock<Settings> = RwLock::new(match Settings::init() {
|
||||||
match Settings::init() {
|
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(e) => panic!("{}", e),
|
Err(e) => panic!("{}", e),
|
||||||
}
|
});
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Settings {
|
impl Settings {
|
||||||
|
@ -96,8 +97,8 @@ impl Settings {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the config as a struct.
|
/// Returns the config as a struct.
|
||||||
pub fn get() -> &'static Self {
|
pub fn get() -> Self {
|
||||||
&SETTINGS
|
SETTINGS.read().unwrap().to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the postgres connection url. If LEMMY_DATABASE_URL is set, that is used,
|
/// Returns the postgres connection url. If LEMMY_DATABASE_URL is set, that is used,
|
||||||
|
@ -119,4 +120,22 @@ impl Settings {
|
||||||
pub fn api_endpoint(&self) -> String {
|
pub fn api_endpoint(&self) -> String {
|
||||||
format!("{}/api/v1", self.hostname)
|
format!("{}/api/v1", self.hostname)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn read_config_file() -> Result<String, Error> {
|
||||||
|
Ok(fs::read_to_string(CONFIG_FILE)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save_config_file(data: &str) -> Result<String, Error> {
|
||||||
|
fs::write(CONFIG_FILE, data)?;
|
||||||
|
|
||||||
|
// Reload the new settings
|
||||||
|
// From https://stackoverflow.com/questions/29654927/how-do-i-assign-a-string-to-a-mutable-static-variable/47181804#47181804
|
||||||
|
let mut new_settings = SETTINGS.write().unwrap();
|
||||||
|
*new_settings = match Settings::init() {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(e) => panic!("{}", e),
|
||||||
|
};
|
||||||
|
|
||||||
|
Self::read_config_file()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
pub const VERSION: &str = "v0.6.44";
|
pub const VERSION: &str = "v0.6.49";
|
||||||
|
|
|
@ -46,4 +46,6 @@ pub enum UserOperation {
|
||||||
GetPrivateMessages,
|
GetPrivateMessages,
|
||||||
UserJoin,
|
UserJoin,
|
||||||
GetComments,
|
GetComments,
|
||||||
|
GetSiteConfig,
|
||||||
|
SaveSiteConfig,
|
||||||
}
|
}
|
||||||
|
|
|
@ -708,6 +708,16 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
|
||||||
res.online = chat.sessions.len();
|
res.online = chat.sessions.len();
|
||||||
to_json_string(&user_operation, &res)
|
to_json_string(&user_operation, &res)
|
||||||
}
|
}
|
||||||
|
UserOperation::GetSiteConfig => {
|
||||||
|
let get_site_config: GetSiteConfig = serde_json::from_str(data)?;
|
||||||
|
let res = Oper::new(get_site_config).perform(&conn)?;
|
||||||
|
to_json_string(&user_operation, &res)
|
||||||
|
}
|
||||||
|
UserOperation::SaveSiteConfig => {
|
||||||
|
let save_site_config: SaveSiteConfig = serde_json::from_str(data)?;
|
||||||
|
let res = Oper::new(save_site_config).perform(&conn)?;
|
||||||
|
to_json_string(&user_operation, &res)
|
||||||
|
}
|
||||||
UserOperation::Search => {
|
UserOperation::Search => {
|
||||||
do_user_operation::<Search, SearchResponse>(user_operation, data, &conn)
|
do_user_operation::<Search, SearchResponse>(user_operation, data, &conn)
|
||||||
}
|
}
|
||||||
|
|
2
ui/assets/css/main.css
vendored
2
ui/assets/css/main.css
vendored
|
@ -156,7 +156,7 @@ hr {
|
||||||
}
|
}
|
||||||
|
|
||||||
.emoji {
|
.emoji {
|
||||||
height: 1.2em !important;
|
max-height: 1.2em !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-wrap-truncate {
|
.text-wrap-truncate {
|
||||||
|
|
33
ui/package.json
vendored
33
ui/package.json
vendored
|
@ -14,19 +14,20 @@
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@joeattardi/emoji-button": "^2.12.1",
|
||||||
"@types/autosize": "^3.0.6",
|
"@types/autosize": "^3.0.6",
|
||||||
"@types/js-cookie": "^2.2.5",
|
"@types/js-cookie": "^2.2.6",
|
||||||
"@types/jwt-decode": "^2.2.1",
|
"@types/jwt-decode": "^2.2.1",
|
||||||
"@types/markdown-it": "^0.0.9",
|
"@types/markdown-it": "^0.0.9",
|
||||||
"@types/markdown-it-container": "^2.0.2",
|
"@types/markdown-it-container": "^2.0.2",
|
||||||
"@types/node": "^13.9.2",
|
"@types/node": "^13.11.1",
|
||||||
"autosize": "^4.0.2",
|
"autosize": "^4.0.2",
|
||||||
"bootswatch": "^4.3.1",
|
"bootswatch": "^4.3.1",
|
||||||
"classcat": "^1.1.3",
|
"classcat": "^4.0.2",
|
||||||
"dotenv": "^8.2.0",
|
"dotenv": "^8.2.0",
|
||||||
"emoji-short-name": "^1.0.0",
|
"emoji-short-name": "^1.0.0",
|
||||||
"husky": "^4.2.3",
|
"husky": "^4.2.5",
|
||||||
"i18next": "^19.3.3",
|
"i18next": "^19.4.1",
|
||||||
"inferno": "^7.4.2",
|
"inferno": "^7.4.2",
|
||||||
"inferno-i18next": "nimbusec-oss/inferno-i18next",
|
"inferno-i18next": "nimbusec-oss/inferno-i18next",
|
||||||
"inferno-router": "^7.4.2",
|
"inferno-router": "^7.4.2",
|
||||||
|
@ -37,26 +38,26 @@
|
||||||
"markdown-it-emoji": "^1.4.0",
|
"markdown-it-emoji": "^1.4.0",
|
||||||
"mobius1-selectr": "^2.4.13",
|
"mobius1-selectr": "^2.4.13",
|
||||||
"moment": "^2.24.0",
|
"moment": "^2.24.0",
|
||||||
"prettier": "^1.18.2",
|
"prettier": "^2.0.4",
|
||||||
"reconnecting-websocket": "^4.4.0",
|
"reconnecting-websocket": "^4.4.0",
|
||||||
"rxjs": "^6.4.0",
|
"rxjs": "^6.5.5",
|
||||||
"terser": "^4.6.7",
|
"terser": "^4.6.11",
|
||||||
"tippy.js": "^6.1.0",
|
"tippy.js": "^6.1.1",
|
||||||
"toastify-js": "^1.7.0",
|
"toastify-js": "^1.7.0",
|
||||||
"tributejs": "^5.1.2",
|
"tributejs": "^5.1.3",
|
||||||
"twemoji": "^12.1.2",
|
"twemoji": "^12.1.2",
|
||||||
"ws": "^7.2.3"
|
"ws": "^7.2.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^6.5.1",
|
"eslint": "^6.5.1",
|
||||||
"eslint-plugin-inferno": "^7.14.3",
|
"eslint-plugin-inferno": "^7.14.3",
|
||||||
"eslint-plugin-jane": "^7.2.0",
|
"eslint-plugin-jane": "^7.2.1",
|
||||||
"fuse-box": "^3.1.3",
|
"fuse-box": "^3.1.3",
|
||||||
"lint-staged": "^10.0.8",
|
"lint-staged": "^10.1.3",
|
||||||
"sortpack": "^2.1.2",
|
"sortpack": "^2.1.4",
|
||||||
"ts-node": "^8.7.0",
|
"ts-node": "^8.8.2",
|
||||||
"ts-transform-classcat": "^0.0.2",
|
"ts-transform-classcat": "^1.0.0",
|
||||||
"ts-transform-inferno": "^4.0.2",
|
"ts-transform-inferno": "^4.0.3",
|
||||||
"typescript": "^3.8.3"
|
"typescript": "^3.8.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
241
ui/src/components/admin-settings.tsx
vendored
Normal file
241
ui/src/components/admin-settings.tsx
vendored
Normal file
|
@ -0,0 +1,241 @@
|
||||||
|
import { Component, linkEvent } from 'inferno';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
import { retryWhen, delay, take } from 'rxjs/operators';
|
||||||
|
import {
|
||||||
|
UserOperation,
|
||||||
|
SiteResponse,
|
||||||
|
GetSiteResponse,
|
||||||
|
SiteConfigForm,
|
||||||
|
GetSiteConfigResponse,
|
||||||
|
WebSocketJsonResponse,
|
||||||
|
} from '../interfaces';
|
||||||
|
import { WebSocketService } from '../services';
|
||||||
|
import { wsJsonToRes, capitalizeFirstLetter, toast, randomStr } from '../utils';
|
||||||
|
import autosize from 'autosize';
|
||||||
|
import { SiteForm } from './site-form';
|
||||||
|
import { UserListing } from './user-listing';
|
||||||
|
import { i18n } from '../i18next';
|
||||||
|
|
||||||
|
interface AdminSettingsState {
|
||||||
|
siteRes: GetSiteResponse;
|
||||||
|
siteConfigRes: GetSiteConfigResponse;
|
||||||
|
siteConfigForm: SiteConfigForm;
|
||||||
|
loading: boolean;
|
||||||
|
siteConfigLoading: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AdminSettings extends Component<any, AdminSettingsState> {
|
||||||
|
private siteConfigTextAreaId = `site-config-${randomStr()}`;
|
||||||
|
private subscription: Subscription;
|
||||||
|
private emptyState: AdminSettingsState = {
|
||||||
|
siteRes: {
|
||||||
|
site: {
|
||||||
|
id: null,
|
||||||
|
name: null,
|
||||||
|
creator_id: null,
|
||||||
|
creator_name: null,
|
||||||
|
published: null,
|
||||||
|
number_of_users: null,
|
||||||
|
number_of_posts: null,
|
||||||
|
number_of_comments: null,
|
||||||
|
number_of_communities: null,
|
||||||
|
enable_downvotes: null,
|
||||||
|
open_registration: null,
|
||||||
|
enable_nsfw: null,
|
||||||
|
},
|
||||||
|
admins: [],
|
||||||
|
banned: [],
|
||||||
|
online: null,
|
||||||
|
},
|
||||||
|
siteConfigForm: {
|
||||||
|
config_hjson: null,
|
||||||
|
auth: null,
|
||||||
|
},
|
||||||
|
siteConfigRes: {
|
||||||
|
config_hjson: null,
|
||||||
|
},
|
||||||
|
loading: true,
|
||||||
|
siteConfigLoading: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(props: any, context: any) {
|
||||||
|
super(props, context);
|
||||||
|
|
||||||
|
this.state = this.emptyState;
|
||||||
|
|
||||||
|
this.subscription = WebSocketService.Instance.subject
|
||||||
|
.pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
|
||||||
|
.subscribe(
|
||||||
|
msg => this.parseMessage(msg),
|
||||||
|
err => console.error(err),
|
||||||
|
() => console.log('complete')
|
||||||
|
);
|
||||||
|
|
||||||
|
WebSocketService.Instance.getSite();
|
||||||
|
WebSocketService.Instance.getSiteConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.subscription.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div class="container">
|
||||||
|
{this.state.loading ? (
|
||||||
|
<h5>
|
||||||
|
<svg class="icon icon-spinner spin">
|
||||||
|
<use xlinkHref="#icon-spinner"></use>
|
||||||
|
</svg>
|
||||||
|
</h5>
|
||||||
|
) : (
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<SiteForm site={this.state.siteRes.site} />
|
||||||
|
{this.admins()}
|
||||||
|
{this.bannedUsers()}
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6">{this.adminSettings()}</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
admins() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h5>{capitalizeFirstLetter(i18n.t('admins'))}</h5>
|
||||||
|
<ul class="list-unstyled">
|
||||||
|
{this.state.siteRes.admins.map(admin => (
|
||||||
|
<li class="list-inline-item">
|
||||||
|
<UserListing
|
||||||
|
user={{
|
||||||
|
name: admin.name,
|
||||||
|
avatar: admin.avatar,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bannedUsers() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h5>{i18n.t('banned_users')}</h5>
|
||||||
|
<ul class="list-unstyled">
|
||||||
|
{this.state.siteRes.banned.map(banned => (
|
||||||
|
<li class="list-inline-item">
|
||||||
|
<UserListing
|
||||||
|
user={{
|
||||||
|
name: banned.name,
|
||||||
|
avatar: banned.avatar,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
adminSettings() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h5>{i18n.t('admin_settings')}</h5>
|
||||||
|
<form onSubmit={linkEvent(this, this.handleSiteConfigSubmit)}>
|
||||||
|
<div class="form-group row">
|
||||||
|
<label
|
||||||
|
class="col-12 col-form-label"
|
||||||
|
htmlFor={this.siteConfigTextAreaId}
|
||||||
|
>
|
||||||
|
{i18n.t('site_config')}
|
||||||
|
</label>
|
||||||
|
<div class="col-12">
|
||||||
|
<textarea
|
||||||
|
id={this.siteConfigTextAreaId}
|
||||||
|
value={this.state.siteConfigForm.config_hjson}
|
||||||
|
onInput={linkEvent(this, this.handleSiteConfigHjsonChange)}
|
||||||
|
class="form-control text-monospace"
|
||||||
|
rows={3}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group row">
|
||||||
|
<div class="col-12">
|
||||||
|
<button type="submit" class="btn btn-secondary mr-2">
|
||||||
|
{this.state.siteConfigLoading ? (
|
||||||
|
<svg class="icon icon-spinner spin">
|
||||||
|
<use xlinkHref="#icon-spinner"></use>
|
||||||
|
</svg>
|
||||||
|
) : (
|
||||||
|
capitalizeFirstLetter(i18n.t('save'))
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSiteConfigSubmit(i: AdminSettings, event: any) {
|
||||||
|
event.preventDefault();
|
||||||
|
i.state.siteConfigLoading = true;
|
||||||
|
WebSocketService.Instance.saveSiteConfig(i.state.siteConfigForm);
|
||||||
|
i.setState(i.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSiteConfigHjsonChange(i: AdminSettings, event: any) {
|
||||||
|
i.state.siteConfigForm.config_hjson = event.target.value;
|
||||||
|
i.setState(i.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
parseMessage(msg: WebSocketJsonResponse) {
|
||||||
|
console.log(msg);
|
||||||
|
let res = wsJsonToRes(msg);
|
||||||
|
if (msg.error) {
|
||||||
|
toast(i18n.t(msg.error), 'danger');
|
||||||
|
this.context.router.history.push('/');
|
||||||
|
this.state.loading = false;
|
||||||
|
this.setState(this.state);
|
||||||
|
return;
|
||||||
|
} else if (msg.reconnect) {
|
||||||
|
} else if (res.op == UserOperation.GetSite) {
|
||||||
|
let data = res.data as GetSiteResponse;
|
||||||
|
|
||||||
|
// This means it hasn't been set up yet
|
||||||
|
if (!data.site) {
|
||||||
|
this.context.router.history.push('/setup');
|
||||||
|
}
|
||||||
|
this.state.siteRes = data;
|
||||||
|
this.setState(this.state);
|
||||||
|
document.title = `${i18n.t('admin_settings')} - ${
|
||||||
|
this.state.siteRes.site.name
|
||||||
|
}`;
|
||||||
|
} else if (res.op == UserOperation.EditSite) {
|
||||||
|
let data = res.data as SiteResponse;
|
||||||
|
this.state.siteRes.site = data.site;
|
||||||
|
this.setState(this.state);
|
||||||
|
toast(i18n.t('site_saved'));
|
||||||
|
} else if (res.op == UserOperation.GetSiteConfig) {
|
||||||
|
let data = res.data as GetSiteConfigResponse;
|
||||||
|
this.state.siteConfigRes = data;
|
||||||
|
this.state.loading = false;
|
||||||
|
this.state.siteConfigForm.config_hjson = this.state.siteConfigRes.config_hjson;
|
||||||
|
this.setState(this.state);
|
||||||
|
var textarea: any = document.getElementById(this.siteConfigTextAreaId);
|
||||||
|
autosize(textarea);
|
||||||
|
} else if (res.op == UserOperation.SaveSiteConfig) {
|
||||||
|
let data = res.data as GetSiteConfigResponse;
|
||||||
|
this.state.siteConfigRes = data;
|
||||||
|
this.state.siteConfigForm.config_hjson = this.state.siteConfigRes.config_hjson;
|
||||||
|
this.state.siteConfigLoading = false;
|
||||||
|
toast(i18n.t('site_saved'));
|
||||||
|
this.setState(this.state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
ui/src/components/comment-form.tsx
vendored
36
ui/src/components/comment-form.tsx
vendored
|
@ -17,10 +17,12 @@ import {
|
||||||
toast,
|
toast,
|
||||||
setupTribute,
|
setupTribute,
|
||||||
wsJsonToRes,
|
wsJsonToRes,
|
||||||
|
emojiPicker,
|
||||||
} from '../utils';
|
} from '../utils';
|
||||||
import { WebSocketService, UserService } from '../services';
|
import { WebSocketService, UserService } from '../services';
|
||||||
import autosize from 'autosize';
|
import autosize from 'autosize';
|
||||||
import Tribute from 'tributejs/src/Tribute.js';
|
import Tribute from 'tributejs/src/Tribute.js';
|
||||||
|
import emojiShortName from 'emoji-short-name';
|
||||||
import { i18n } from '../i18next';
|
import { i18n } from '../i18next';
|
||||||
|
|
||||||
interface CommentFormProps {
|
interface CommentFormProps {
|
||||||
|
@ -69,6 +71,8 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this.tribute = setupTribute();
|
this.tribute = setupTribute();
|
||||||
|
this.setupEmojiPicker();
|
||||||
|
|
||||||
this.state = this.emptyState;
|
this.state = this.emptyState;
|
||||||
|
|
||||||
if (this.props.node) {
|
if (this.props.node) {
|
||||||
|
@ -158,8 +162,9 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
|
||||||
</button>
|
</button>
|
||||||
{this.state.commentForm.content && (
|
{this.state.commentForm.content && (
|
||||||
<button
|
<button
|
||||||
className={`btn btn-sm mr-2 btn-secondary ${this.state
|
className={`btn btn-sm mr-2 btn-secondary ${
|
||||||
.previewMode && 'active'}`}
|
this.state.previewMode && 'active'
|
||||||
|
}`}
|
||||||
onClick={linkEvent(this, this.handlePreviewToggle)}
|
onClick={linkEvent(this, this.handlePreviewToggle)}
|
||||||
>
|
>
|
||||||
{i18n.t('preview')}
|
{i18n.t('preview')}
|
||||||
|
@ -209,6 +214,15 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
|
||||||
<use xlinkHref="#icon-spinner"></use>
|
<use xlinkHref="#icon-spinner"></use>
|
||||||
</svg>
|
</svg>
|
||||||
)}
|
)}
|
||||||
|
<span
|
||||||
|
onClick={linkEvent(this, this.handleEmojiPickerClick)}
|
||||||
|
class="pointer unselectable d-inline-block mr-3 float-right text-muted font-weight-bold"
|
||||||
|
data-tippy-content={i18n.t('emoji_picker')}
|
||||||
|
>
|
||||||
|
<svg class="icon icon-inline">
|
||||||
|
<use xlinkHref="#icon-smile"></use>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -216,6 +230,20 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setupEmojiPicker() {
|
||||||
|
emojiPicker.on('emoji', twemojiHtmlStr => {
|
||||||
|
if (this.state.commentForm.content == null) {
|
||||||
|
this.state.commentForm.content = '';
|
||||||
|
}
|
||||||
|
var el = document.createElement('div');
|
||||||
|
el.innerHTML = twemojiHtmlStr;
|
||||||
|
let nativeUnicode = (el.childNodes[0] as HTMLElement).getAttribute('alt');
|
||||||
|
let shortName = `:${emojiShortName[nativeUnicode]}:`;
|
||||||
|
this.state.commentForm.content += shortName;
|
||||||
|
this.setState(this.state);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
handleFinished() {
|
handleFinished() {
|
||||||
this.state.previewMode = false;
|
this.state.previewMode = false;
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
|
@ -242,6 +270,10 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleEmojiPickerClick(_i: CommentForm, event: any) {
|
||||||
|
emojiPicker.togglePicker(event.target);
|
||||||
|
}
|
||||||
|
|
||||||
handleCommentContentChange(i: CommentForm, event: any) {
|
handleCommentContentChange(i: CommentForm, event: any) {
|
||||||
i.state.commentForm.content = event.target.value;
|
i.state.commentForm.content = event.target.value;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
|
|
51
ui/src/components/comment-node.tsx
vendored
51
ui/src/components/comment-node.tsx
vendored
|
@ -24,8 +24,6 @@ import {
|
||||||
getUnixTime,
|
getUnixTime,
|
||||||
canMod,
|
canMod,
|
||||||
isMod,
|
isMod,
|
||||||
pictshareAvatarThumbnail,
|
|
||||||
showAvatars,
|
|
||||||
setupTippy,
|
setupTippy,
|
||||||
colorList,
|
colorList,
|
||||||
} from '../utils';
|
} from '../utils';
|
||||||
|
@ -33,6 +31,7 @@ import moment from 'moment';
|
||||||
import { MomentTime } from './moment-time';
|
import { MomentTime } from './moment-time';
|
||||||
import { CommentForm } from './comment-form';
|
import { CommentForm } from './comment-form';
|
||||||
import { CommentNodes } from './comment-nodes';
|
import { CommentNodes } from './comment-nodes';
|
||||||
|
import { UserListing } from './user-listing';
|
||||||
import { i18n } from '../i18next';
|
import { i18n } from '../i18next';
|
||||||
|
|
||||||
interface CommentNodeState {
|
interface CommentNodeState {
|
||||||
|
@ -143,25 +142,21 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class={`${!this.props.noIndent &&
|
class={`${
|
||||||
|
!this.props.noIndent &&
|
||||||
this.props.node.comment.parent_id &&
|
this.props.node.comment.parent_id &&
|
||||||
'ml-2'}`}
|
'ml-2'
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<div class="d-flex flex-wrap align-items-center mb-1 mt-1 text-muted small">
|
<div class="d-flex flex-wrap align-items-center mb-1 mt-1 text-muted small">
|
||||||
<Link
|
<span class="mr-2">
|
||||||
className="mr-2 text-body font-weight-bold"
|
<UserListing
|
||||||
to={`/u/${node.comment.creator_name}`}
|
user={{
|
||||||
>
|
name: node.comment.creator_name,
|
||||||
{node.comment.creator_avatar && showAvatars() && (
|
avatar: node.comment.creator_avatar,
|
||||||
<img
|
}}
|
||||||
height="32"
|
|
||||||
width="32"
|
|
||||||
src={pictshareAvatarThumbnail(node.comment.creator_avatar)}
|
|
||||||
class="rounded-circle mr-1"
|
|
||||||
/>
|
/>
|
||||||
)}
|
</span>
|
||||||
<span>{node.comment.creator_name}</span>
|
|
||||||
</Link>
|
|
||||||
{this.isMod && (
|
{this.isMod && (
|
||||||
<div className="badge badge-light d-none d-sm-inline mr-2">
|
<div className="badge badge-light d-none d-sm-inline mr-2">
|
||||||
{i18n.t('mod')}
|
{i18n.t('mod')}
|
||||||
|
@ -191,7 +186,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<div
|
<div
|
||||||
className="mr-lg-4 flex-grow-1 flex-lg-grow-0 unselectable pointer mr-2"
|
className="mr-lg-4 flex-grow-1 flex-lg-grow-0 unselectable pointer mx-2"
|
||||||
onClick={linkEvent(this, this.handleCommentCollapse)}
|
onClick={linkEvent(this, this.handleCommentCollapse)}
|
||||||
>
|
>
|
||||||
{this.state.collapsed ? (
|
{this.state.collapsed ? (
|
||||||
|
@ -256,8 +251,9 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
this.loadingIcon
|
this.loadingIcon
|
||||||
) : (
|
) : (
|
||||||
<svg
|
<svg
|
||||||
class={`icon icon-inline ${node.comment.read &&
|
class={`icon icon-inline ${
|
||||||
'text-success'}`}
|
node.comment.read && 'text-success'
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<use xlinkHref="#icon-check"></use>
|
<use xlinkHref="#icon-check"></use>
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -309,8 +305,9 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
this.loadingIcon
|
this.loadingIcon
|
||||||
) : (
|
) : (
|
||||||
<svg
|
<svg
|
||||||
class={`icon icon-inline ${node.comment.saved &&
|
class={`icon icon-inline ${
|
||||||
'text-warning'}`}
|
node.comment.saved && 'text-warning'
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<use xlinkHref="#icon-star"></use>
|
<use xlinkHref="#icon-star"></use>
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -357,8 +354,9 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
data-tippy-content={i18n.t('view_source')}
|
data-tippy-content={i18n.t('view_source')}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
class={`icon icon-inline ${this.state
|
class={`icon icon-inline ${
|
||||||
.viewSource && 'text-success'}`}
|
this.state.viewSource && 'text-success'
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<use xlinkHref="#icon-file-text"></use>
|
<use xlinkHref="#icon-file-text"></use>
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -387,8 +385,9 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
class={`icon icon-inline ${node.comment
|
class={`icon icon-inline ${
|
||||||
.deleted && 'text-danger'}`}
|
node.comment.deleted && 'text-danger'
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<use xlinkHref="#icon-trash"></use>
|
<use xlinkHref="#icon-trash"></use>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
22
ui/src/components/main.tsx
vendored
22
ui/src/components/main.tsx
vendored
|
@ -33,13 +33,12 @@ import { SortSelect } from './sort-select';
|
||||||
import { ListingTypeSelect } from './listing-type-select';
|
import { ListingTypeSelect } from './listing-type-select';
|
||||||
import { DataTypeSelect } from './data-type-select';
|
import { DataTypeSelect } from './data-type-select';
|
||||||
import { SiteForm } from './site-form';
|
import { SiteForm } from './site-form';
|
||||||
|
import { UserListing } from './user-listing';
|
||||||
import {
|
import {
|
||||||
wsJsonToRes,
|
wsJsonToRes,
|
||||||
repoUrl,
|
repoUrl,
|
||||||
mdToHtml,
|
mdToHtml,
|
||||||
fetchLimit,
|
fetchLimit,
|
||||||
pictshareAvatarThumbnail,
|
|
||||||
showAvatars,
|
|
||||||
toast,
|
toast,
|
||||||
getListingTypeFromProps,
|
getListingTypeFromProps,
|
||||||
getPageFromProps,
|
getPageFromProps,
|
||||||
|
@ -316,20 +315,12 @@ export class Main extends Component<any, MainState> {
|
||||||
<li class="list-inline-item">{i18n.t('admins')}:</li>
|
<li class="list-inline-item">{i18n.t('admins')}:</li>
|
||||||
{this.state.siteRes.admins.map(admin => (
|
{this.state.siteRes.admins.map(admin => (
|
||||||
<li class="list-inline-item">
|
<li class="list-inline-item">
|
||||||
<Link
|
<UserListing
|
||||||
class="text-body font-weight-bold"
|
user={{
|
||||||
to={`/u/${admin.name}`}
|
name: admin.name,
|
||||||
>
|
avatar: admin.avatar,
|
||||||
{admin.avatar && showAvatars() && (
|
}}
|
||||||
<img
|
|
||||||
height="32"
|
|
||||||
width="32"
|
|
||||||
src={pictshareAvatarThumbnail(admin.avatar)}
|
|
||||||
class="rounded-circle mr-1"
|
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
<span>{admin.name}</span>
|
|
||||||
</Link>
|
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -619,6 +610,7 @@ export class Main extends Component<any, MainState> {
|
||||||
this.state.siteRes.site = data.site;
|
this.state.siteRes.site = data.site;
|
||||||
this.state.showEditSite = false;
|
this.state.showEditSite = false;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
|
toast(i18n.t('site_saved'));
|
||||||
} else if (res.op == UserOperation.GetPosts) {
|
} else if (res.op == UserOperation.GetPosts) {
|
||||||
let data = res.data as GetPostsResponse;
|
let data = res.data as GetPostsResponse;
|
||||||
this.state.posts = data.posts;
|
this.state.posts = data.posts;
|
||||||
|
|
26
ui/src/components/navbar.tsx
vendored
26
ui/src/components/navbar.tsx
vendored
|
@ -16,6 +16,7 @@ import {
|
||||||
Comment,
|
Comment,
|
||||||
CommentResponse,
|
CommentResponse,
|
||||||
PrivateMessage,
|
PrivateMessage,
|
||||||
|
UserView,
|
||||||
PrivateMessageResponse,
|
PrivateMessageResponse,
|
||||||
WebSocketJsonResponse,
|
WebSocketJsonResponse,
|
||||||
} from '../interfaces';
|
} from '../interfaces';
|
||||||
|
@ -40,6 +41,7 @@ interface NavbarState {
|
||||||
messages: Array<PrivateMessage>;
|
messages: Array<PrivateMessage>;
|
||||||
unreadCount: number;
|
unreadCount: number;
|
||||||
siteName: string;
|
siteName: string;
|
||||||
|
admins: Array<UserView>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Navbar extends Component<any, NavbarState> {
|
export class Navbar extends Component<any, NavbarState> {
|
||||||
|
@ -53,6 +55,7 @@ export class Navbar extends Component<any, NavbarState> {
|
||||||
messages: [],
|
messages: [],
|
||||||
expanded: false,
|
expanded: false,
|
||||||
siteName: undefined,
|
siteName: undefined,
|
||||||
|
admins: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
|
@ -179,6 +182,19 @@ export class Navbar extends Component<any, NavbarState> {
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="navbar-nav ml-auto">
|
<ul class="navbar-nav ml-auto">
|
||||||
|
{this.canAdmin && (
|
||||||
|
<li className="nav-item mt-1">
|
||||||
|
<Link
|
||||||
|
class="nav-link"
|
||||||
|
to={`/admin`}
|
||||||
|
title={i18n.t('admin_settings')}
|
||||||
|
>
|
||||||
|
<svg class="icon">
|
||||||
|
<use xlinkHref="#icon-settings"></use>
|
||||||
|
</svg>
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
{this.state.isLoggedIn ? (
|
{this.state.isLoggedIn ? (
|
||||||
<>
|
<>
|
||||||
<li className="nav-item mt-1">
|
<li className="nav-item mt-1">
|
||||||
|
@ -298,7 +314,10 @@ export class Navbar extends Component<any, NavbarState> {
|
||||||
|
|
||||||
if (data.site && !this.state.siteName) {
|
if (data.site && !this.state.siteName) {
|
||||||
this.state.siteName = data.site.name;
|
this.state.siteName = data.site.name;
|
||||||
|
this.state.admins = data.admins;
|
||||||
WebSocketService.Instance.site = data.site;
|
WebSocketService.Instance.site = data.site;
|
||||||
|
WebSocketService.Instance.admins = data.admins;
|
||||||
|
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -353,6 +372,13 @@ export class Navbar extends Component<any, NavbarState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get canAdmin(): boolean {
|
||||||
|
return (
|
||||||
|
UserService.Instance.user &&
|
||||||
|
this.state.admins.map(a => a.id).includes(UserService.Instance.user.id)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
requestNotificationPermission() {
|
requestNotificationPermission() {
|
||||||
if (UserService.Instance.user) {
|
if (UserService.Instance.user) {
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
|
43
ui/src/components/post-form.tsx
vendored
43
ui/src/components/post-form.tsx
vendored
|
@ -34,9 +34,11 @@ import {
|
||||||
randomStr,
|
randomStr,
|
||||||
setupTribute,
|
setupTribute,
|
||||||
setupTippy,
|
setupTippy,
|
||||||
|
emojiPicker,
|
||||||
} from '../utils';
|
} from '../utils';
|
||||||
import autosize from 'autosize';
|
import autosize from 'autosize';
|
||||||
import Tribute from 'tributejs/src/Tribute.js';
|
import Tribute from 'tributejs/src/Tribute.js';
|
||||||
|
import emojiShortName from 'emoji-short-name';
|
||||||
import Selectr from 'mobius1-selectr';
|
import Selectr from 'mobius1-selectr';
|
||||||
import { i18n } from '../i18next';
|
import { i18n } from '../i18next';
|
||||||
|
|
||||||
|
@ -92,6 +94,8 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
this.fetchPageTitle = debounce(this.fetchPageTitle).bind(this);
|
this.fetchPageTitle = debounce(this.fetchPageTitle).bind(this);
|
||||||
|
|
||||||
this.tribute = setupTribute();
|
this.tribute = setupTribute();
|
||||||
|
this.setupEmojiPicker();
|
||||||
|
|
||||||
this.state = this.emptyState;
|
this.state = this.emptyState;
|
||||||
|
|
||||||
if (this.props.post) {
|
if (this.props.post) {
|
||||||
|
@ -190,8 +194,9 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
<form>
|
<form>
|
||||||
<label
|
<label
|
||||||
htmlFor="file-upload"
|
htmlFor="file-upload"
|
||||||
className={`${UserService.Instance.user &&
|
className={`${
|
||||||
'pointer'} d-inline-block float-right text-muted h6 font-weight-bold`}
|
UserService.Instance.user && 'pointer'
|
||||||
|
} d-inline-block float-right text-muted font-weight-bold`}
|
||||||
data-tippy-content={i18n.t('upload_image')}
|
data-tippy-content={i18n.t('upload_image')}
|
||||||
>
|
>
|
||||||
<svg class="icon icon-inline">
|
<svg class="icon icon-inline">
|
||||||
|
@ -284,8 +289,9 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
)}
|
)}
|
||||||
{this.state.postForm.body && (
|
{this.state.postForm.body && (
|
||||||
<button
|
<button
|
||||||
className={`mt-1 mr-2 btn btn-sm btn-secondary ${this.state
|
className={`mt-1 mr-2 btn btn-sm btn-secondary ${
|
||||||
.previewMode && 'active'}`}
|
this.state.previewMode && 'active'
|
||||||
|
}`}
|
||||||
onClick={linkEvent(this, this.handlePreviewToggle)}
|
onClick={linkEvent(this, this.handlePreviewToggle)}
|
||||||
>
|
>
|
||||||
{i18n.t('preview')}
|
{i18n.t('preview')}
|
||||||
|
@ -294,13 +300,22 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
<a
|
<a
|
||||||
href={markdownHelpUrl}
|
href={markdownHelpUrl}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
class="d-inline-block float-right text-muted h6 font-weight-bold"
|
class="d-inline-block float-right text-muted font-weight-bold"
|
||||||
title={i18n.t('formatting_help')}
|
title={i18n.t('formatting_help')}
|
||||||
>
|
>
|
||||||
<svg class="icon icon-inline">
|
<svg class="icon icon-inline">
|
||||||
<use xlinkHref="#icon-help-circle"></use>
|
<use xlinkHref="#icon-help-circle"></use>
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
|
<span
|
||||||
|
onClick={linkEvent(this, this.handleEmojiPickerClick)}
|
||||||
|
class="pointer unselectable d-inline-block mr-3 float-right text-muted font-weight-bold"
|
||||||
|
data-tippy-content={i18n.t('emoji_picker')}
|
||||||
|
>
|
||||||
|
<svg class="icon icon-inline">
|
||||||
|
<use xlinkHref="#icon-smile"></use>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{!this.props.post && (
|
{!this.props.post && (
|
||||||
|
@ -369,6 +384,20 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setupEmojiPicker() {
|
||||||
|
emojiPicker.on('emoji', twemojiHtmlStr => {
|
||||||
|
if (this.state.postForm.body == null) {
|
||||||
|
this.state.postForm.body = '';
|
||||||
|
}
|
||||||
|
var el = document.createElement('div');
|
||||||
|
el.innerHTML = twemojiHtmlStr;
|
||||||
|
let nativeUnicode = (el.childNodes[0] as HTMLElement).getAttribute('alt');
|
||||||
|
let shortName = `:${emojiShortName[nativeUnicode]}:`;
|
||||||
|
this.state.postForm.body += shortName;
|
||||||
|
this.setState(this.state);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
handlePostSubmit(i: PostForm, event: any) {
|
handlePostSubmit(i: PostForm, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (i.props.post) {
|
if (i.props.post) {
|
||||||
|
@ -512,6 +541,10 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleEmojiPickerClick(_i: PostForm, event: any) {
|
||||||
|
emojiPicker.togglePicker(event.target);
|
||||||
|
}
|
||||||
|
|
||||||
parseMessage(msg: WebSocketJsonResponse) {
|
parseMessage(msg: WebSocketJsonResponse) {
|
||||||
let res = wsJsonToRes(msg);
|
let res = wsJsonToRes(msg);
|
||||||
if (msg.error) {
|
if (msg.error) {
|
||||||
|
|
41
ui/src/components/post-listing.tsx
vendored
41
ui/src/components/post-listing.tsx
vendored
|
@ -19,18 +19,19 @@ import {
|
||||||
import { MomentTime } from './moment-time';
|
import { MomentTime } from './moment-time';
|
||||||
import { PostForm } from './post-form';
|
import { PostForm } from './post-form';
|
||||||
import { IFramelyCard } from './iframely-card';
|
import { IFramelyCard } from './iframely-card';
|
||||||
|
import { UserListing } from './user-listing';
|
||||||
import {
|
import {
|
||||||
|
md,
|
||||||
mdToHtml,
|
mdToHtml,
|
||||||
canMod,
|
canMod,
|
||||||
isMod,
|
isMod,
|
||||||
isImage,
|
isImage,
|
||||||
isVideo,
|
isVideo,
|
||||||
getUnixTime,
|
getUnixTime,
|
||||||
pictshareAvatarThumbnail,
|
|
||||||
showAvatars,
|
|
||||||
pictshareImage,
|
pictshareImage,
|
||||||
setupTippy,
|
setupTippy,
|
||||||
hostname,
|
hostname,
|
||||||
|
previewLines,
|
||||||
} from '../utils';
|
} from '../utils';
|
||||||
import { i18n } from '../i18next';
|
import { i18n } from '../i18next';
|
||||||
|
|
||||||
|
@ -415,20 +416,12 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
<ul class="list-inline mb-0 text-muted small">
|
<ul class="list-inline mb-0 text-muted small">
|
||||||
<li className="list-inline-item">
|
<li className="list-inline-item">
|
||||||
<span>{i18n.t('by')} </span>
|
<span>{i18n.t('by')} </span>
|
||||||
<Link
|
<UserListing
|
||||||
className="text-body font-weight-bold"
|
user={{
|
||||||
to={`/u/${post.creator_name}`}
|
name: post.creator_name,
|
||||||
>
|
avatar: post.creator_avatar,
|
||||||
{post.creator_avatar && showAvatars() && (
|
}}
|
||||||
<img
|
|
||||||
height="32"
|
|
||||||
width="32"
|
|
||||||
src={pictshareAvatarThumbnail(post.creator_avatar)}
|
|
||||||
class="rounded-circle mr-1"
|
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
<span>{post.creator_name}</span>
|
|
||||||
</Link>
|
|
||||||
{this.isMod && (
|
{this.isMod && (
|
||||||
<span className="mx-1 badge badge-light">
|
<span className="mx-1 badge badge-light">
|
||||||
{i18n.t('mod')}
|
{i18n.t('mod')}
|
||||||
|
@ -465,6 +458,24 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
<MomentTime data={post} />
|
<MomentTime data={post} />
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
|
{post.body && (
|
||||||
|
<>
|
||||||
|
<li className="list-inline-item">•</li>
|
||||||
|
<li className="list-inline-item">
|
||||||
|
{/* Using a link with tippy doesn't work on touch devices unfortunately */}
|
||||||
|
<Link
|
||||||
|
className="text-muted"
|
||||||
|
data-tippy-content={md.render(previewLines(post.body))}
|
||||||
|
data-tippy-allowHtml={true}
|
||||||
|
to={`/post/${post.id}`}
|
||||||
|
>
|
||||||
|
<svg class="mr-1 icon icon-inline">
|
||||||
|
<use xlinkHref="#icon-book-open"></use>
|
||||||
|
</svg>
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<li className="list-inline-item">•</li>
|
<li className="list-inline-item">•</li>
|
||||||
{this.state.upvotes !== this.state.score && (
|
{this.state.upvotes !== this.state.score && (
|
||||||
<>
|
<>
|
||||||
|
|
22
ui/src/components/post.tsx
vendored
22
ui/src/components/post.tsx
vendored
|
@ -213,8 +213,9 @@ export class Post extends Component<any, PostState> {
|
||||||
return (
|
return (
|
||||||
<div class="btn-group btn-group-toggle mb-2">
|
<div class="btn-group btn-group-toggle mb-2">
|
||||||
<label
|
<label
|
||||||
className={`btn btn-sm btn-secondary pointer ${this.state
|
className={`btn btn-sm btn-secondary pointer ${
|
||||||
.commentSort === CommentSortType.Hot && 'active'}`}
|
this.state.commentSort === CommentSortType.Hot && 'active'
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
{i18n.t('hot')}
|
{i18n.t('hot')}
|
||||||
<input
|
<input
|
||||||
|
@ -225,8 +226,9 @@ export class Post extends Component<any, PostState> {
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<label
|
<label
|
||||||
className={`btn btn-sm btn-secondary pointer ${this.state
|
className={`btn btn-sm btn-secondary pointer ${
|
||||||
.commentSort === CommentSortType.Top && 'active'}`}
|
this.state.commentSort === CommentSortType.Top && 'active'
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
{i18n.t('top')}
|
{i18n.t('top')}
|
||||||
<input
|
<input
|
||||||
|
@ -237,8 +239,9 @@ export class Post extends Component<any, PostState> {
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<label
|
<label
|
||||||
className={`btn btn-sm btn-secondary pointer ${this.state
|
className={`btn btn-sm btn-secondary pointer ${
|
||||||
.commentSort === CommentSortType.New && 'active'}`}
|
this.state.commentSort === CommentSortType.New && 'active'
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
{i18n.t('new')}
|
{i18n.t('new')}
|
||||||
<input
|
<input
|
||||||
|
@ -249,8 +252,9 @@ export class Post extends Component<any, PostState> {
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<label
|
<label
|
||||||
className={`btn btn-sm btn-secondary pointer ${this.state
|
className={`btn btn-sm btn-secondary pointer ${
|
||||||
.commentSort === CommentSortType.Old && 'active'}`}
|
this.state.commentSort === CommentSortType.Old && 'active'
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
{i18n.t('old')}
|
{i18n.t('old')}
|
||||||
<input
|
<input
|
||||||
|
@ -460,7 +464,7 @@ export class Post extends Component<any, PostState> {
|
||||||
} else if (res.op == UserOperation.Search) {
|
} else if (res.op == UserOperation.Search) {
|
||||||
let data = res.data as SearchResponse;
|
let data = res.data as SearchResponse;
|
||||||
this.state.crossPosts = data.posts.filter(
|
this.state.crossPosts = data.posts.filter(
|
||||||
p => p.id != this.state.post.id
|
p => p.id != Number(this.props.match.params.id)
|
||||||
);
|
);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (res.op == UserOperation.TransferSite) {
|
} else if (res.op == UserOperation.TransferSite) {
|
||||||
|
|
28
ui/src/components/private-message-form.tsx
vendored
28
ui/src/components/private-message-form.tsx
vendored
|
@ -21,14 +21,13 @@ import {
|
||||||
capitalizeFirstLetter,
|
capitalizeFirstLetter,
|
||||||
markdownHelpUrl,
|
markdownHelpUrl,
|
||||||
mdToHtml,
|
mdToHtml,
|
||||||
showAvatars,
|
|
||||||
pictshareAvatarThumbnail,
|
|
||||||
wsJsonToRes,
|
wsJsonToRes,
|
||||||
toast,
|
toast,
|
||||||
randomStr,
|
randomStr,
|
||||||
setupTribute,
|
setupTribute,
|
||||||
setupTippy,
|
setupTippy,
|
||||||
} from '../utils';
|
} from '../utils';
|
||||||
|
import { UserListing } from './user-listing';
|
||||||
import Tribute from 'tributejs/src/Tribute.js';
|
import Tribute from 'tributejs/src/Tribute.js';
|
||||||
import autosize from 'autosize';
|
import autosize from 'autosize';
|
||||||
import { i18n } from '../i18next';
|
import { i18n } from '../i18next';
|
||||||
|
@ -132,22 +131,12 @@ export class PrivateMessageForm extends Component<
|
||||||
|
|
||||||
{this.state.recipient && (
|
{this.state.recipient && (
|
||||||
<div class="col-sm-10 form-control-plaintext">
|
<div class="col-sm-10 form-control-plaintext">
|
||||||
<Link
|
<UserListing
|
||||||
className="text-body font-weight-bold"
|
user={{
|
||||||
to={`/u/${this.state.recipient.name}`}
|
name: this.state.recipient.name,
|
||||||
>
|
avatar: this.state.recipient.avatar,
|
||||||
{this.state.recipient.avatar && showAvatars() && (
|
}}
|
||||||
<img
|
|
||||||
height="32"
|
|
||||||
width="32"
|
|
||||||
src={pictshareAvatarThumbnail(
|
|
||||||
this.state.recipient.avatar
|
|
||||||
)}
|
|
||||||
class="rounded-circle mr-1"
|
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
<span>{this.state.recipient.name}</span>
|
|
||||||
</Link>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -233,8 +222,9 @@ export class PrivateMessageForm extends Component<
|
||||||
</button>
|
</button>
|
||||||
{this.state.privateMessageForm.content && (
|
{this.state.privateMessageForm.content && (
|
||||||
<button
|
<button
|
||||||
className={`btn btn-secondary mr-2 ${this.state.previewMode &&
|
className={`btn btn-secondary mr-2 ${
|
||||||
'active'}`}
|
this.state.previewMode && 'active'
|
||||||
|
}`}
|
||||||
onClick={linkEvent(this, this.handlePreviewToggle)}
|
onClick={linkEvent(this, this.handlePreviewToggle)}
|
||||||
>
|
>
|
||||||
{i18n.t('preview')}
|
{i18n.t('preview')}
|
||||||
|
|
16
ui/src/components/private-message.tsx
vendored
16
ui/src/components/private-message.tsx
vendored
|
@ -58,6 +58,7 @@ export class PrivateMessage extends Component<
|
||||||
<div class="border-top border-light">
|
<div class="border-top border-light">
|
||||||
<div>
|
<div>
|
||||||
<ul class="list-inline mb-0 text-muted small">
|
<ul class="list-inline mb-0 text-muted small">
|
||||||
|
{/* TODO refactor this */}
|
||||||
<li className="list-inline-item">
|
<li className="list-inline-item">
|
||||||
{this.mine ? i18n.t('to') : i18n.t('from')}
|
{this.mine ? i18n.t('to') : i18n.t('from')}
|
||||||
</li>
|
</li>
|
||||||
|
@ -143,8 +144,9 @@ export class PrivateMessage extends Component<
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
class={`icon icon-inline ${message.read &&
|
class={`icon icon-inline ${
|
||||||
'text-success'}`}
|
message.read && 'text-success'
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<use xlinkHref="#icon-check"></use>
|
<use xlinkHref="#icon-check"></use>
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -187,8 +189,9 @@ export class PrivateMessage extends Component<
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
class={`icon icon-inline ${message.deleted &&
|
class={`icon icon-inline ${
|
||||||
'text-danger'}`}
|
message.deleted && 'text-danger'
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<use xlinkHref="#icon-trash"></use>
|
<use xlinkHref="#icon-trash"></use>
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -203,8 +206,9 @@ export class PrivateMessage extends Component<
|
||||||
data-tippy-content={i18n.t('view_source')}
|
data-tippy-content={i18n.t('view_source')}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
class={`icon icon-inline ${this.state.viewSource &&
|
class={`icon icon-inline ${
|
||||||
'text-success'}`}
|
this.state.viewSource && 'text-success'
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<use xlinkHref="#icon-file-text"></use>
|
<use xlinkHref="#icon-file-text"></use>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
21
ui/src/components/search.tsx
vendored
21
ui/src/components/search.tsx
vendored
|
@ -30,6 +30,7 @@ import {
|
||||||
commentsToFlatNodes,
|
commentsToFlatNodes,
|
||||||
} from '../utils';
|
} from '../utils';
|
||||||
import { PostListing } from './post-listing';
|
import { PostListing } from './post-listing';
|
||||||
|
import { UserListing } from './user-listing';
|
||||||
import { SortSelect } from './sort-select';
|
import { SortSelect } from './sort-select';
|
||||||
import { CommentNodes } from './comment-nodes';
|
import { CommentNodes } from './comment-nodes';
|
||||||
import { i18n } from '../i18next';
|
import { i18n } from '../i18next';
|
||||||
|
@ -266,22 +267,12 @@ export class Search extends Component<any, SearchState> {
|
||||||
{i.type_ == 'users' && (
|
{i.type_ == 'users' && (
|
||||||
<div>
|
<div>
|
||||||
<span>
|
<span>
|
||||||
<Link
|
<UserListing
|
||||||
className="text-info"
|
user={{
|
||||||
to={`/u/${(i.data as UserView).name}`}
|
name: (i.data as UserView).name,
|
||||||
>
|
avatar: (i.data as UserView).avatar,
|
||||||
{(i.data as UserView).avatar && showAvatars() && (
|
}}
|
||||||
<img
|
|
||||||
height="32"
|
|
||||||
width="32"
|
|
||||||
src={pictshareAvatarThumbnail(
|
|
||||||
(i.data as UserView).avatar
|
|
||||||
)}
|
|
||||||
class="rounded-circle mr-1"
|
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
<span>{`/u/${(i.data as UserView).name}`}</span>
|
|
||||||
</Link>
|
|
||||||
</span>
|
</span>
|
||||||
<span>{` - ${
|
<span>{` - ${
|
||||||
(i.data as UserView).comment_score
|
(i.data as UserView).comment_score
|
||||||
|
|
30
ui/src/components/sidebar.tsx
vendored
30
ui/src/components/sidebar.tsx
vendored
|
@ -15,6 +15,7 @@ import {
|
||||||
showAvatars,
|
showAvatars,
|
||||||
} from '../utils';
|
} from '../utils';
|
||||||
import { CommunityForm } from './community-form';
|
import { CommunityForm } from './community-form';
|
||||||
|
import { UserListing } from './user-listing';
|
||||||
import { i18n } from '../i18next';
|
import { i18n } from '../i18next';
|
||||||
|
|
||||||
interface SidebarProps {
|
interface SidebarProps {
|
||||||
|
@ -110,8 +111,9 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
class={`icon icon-inline ${community.deleted &&
|
class={`icon icon-inline ${
|
||||||
'text-danger'}`}
|
community.deleted && 'text-danger'
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<use xlinkHref="#icon-trash"></use>
|
<use xlinkHref="#icon-trash"></use>
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -204,27 +206,19 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
<li class="list-inline-item">{i18n.t('mods')}: </li>
|
<li class="list-inline-item">{i18n.t('mods')}: </li>
|
||||||
{this.props.moderators.map(mod => (
|
{this.props.moderators.map(mod => (
|
||||||
<li class="list-inline-item">
|
<li class="list-inline-item">
|
||||||
<Link
|
<UserListing
|
||||||
class="text-body font-weight-bold"
|
user={{
|
||||||
to={`/u/${mod.user_name}`}
|
name: mod.user_name,
|
||||||
>
|
avatar: mod.avatar,
|
||||||
{mod.avatar && showAvatars() && (
|
}}
|
||||||
<img
|
|
||||||
height="32"
|
|
||||||
width="32"
|
|
||||||
src={pictshareAvatarThumbnail(mod.avatar)}
|
|
||||||
class="rounded-circle mr-1"
|
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
<span>{mod.user_name}</span>
|
|
||||||
</Link>
|
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
<Link
|
<Link
|
||||||
class={`btn btn-sm btn-secondary btn-block mb-3 ${(community.deleted ||
|
class={`btn btn-sm btn-secondary btn-block mb-3 ${
|
||||||
community.removed) &&
|
(community.deleted || community.removed) && 'no-click'
|
||||||
'no-click'}`}
|
}`}
|
||||||
to={`/create_post?community=${community.name}`}
|
to={`/create_post?community=${community.name}`}
|
||||||
>
|
>
|
||||||
{i18n.t('create_a_post')}
|
{i18n.t('create_a_post')}
|
||||||
|
|
7
ui/src/components/site-form.tsx
vendored
7
ui/src/components/site-form.tsx
vendored
|
@ -58,12 +58,19 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Necessary to stop the loading
|
||||||
|
componentWillReceiveProps() {
|
||||||
|
this.state.loading = false;
|
||||||
|
this.setState(this.state);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Prompt
|
<Prompt
|
||||||
when={
|
when={
|
||||||
!this.state.loading &&
|
!this.state.loading &&
|
||||||
|
!this.props.site &&
|
||||||
(this.state.siteForm.name || this.state.siteForm.description)
|
(this.state.siteForm.name || this.state.siteForm.description)
|
||||||
}
|
}
|
||||||
message={i18n.t('block_leaving')}
|
message={i18n.t('block_leaving')}
|
||||||
|
|
11
ui/src/components/sponsors.tsx
vendored
11
ui/src/components/sponsors.tsx
vendored
|
@ -2,11 +2,12 @@ import { Component } from 'inferno';
|
||||||
import { WebSocketService } from '../services';
|
import { WebSocketService } from '../services';
|
||||||
import { i18n } from '../i18next';
|
import { i18n } from '../i18next';
|
||||||
import { T } from 'inferno-i18next';
|
import { T } from 'inferno-i18next';
|
||||||
|
import { repoUrl } from '../utils';
|
||||||
|
|
||||||
let general = [
|
let general = [
|
||||||
|
'alexx henry',
|
||||||
'Nathan J. Goode',
|
'Nathan J. Goode',
|
||||||
'Andre Vallestero',
|
'Andre Vallestero',
|
||||||
'riccardo',
|
|
||||||
'NotTooHighToHack',
|
'NotTooHighToHack',
|
||||||
];
|
];
|
||||||
let highlighted = ['Alex Benishek'];
|
let highlighted = ['Alex Benishek'];
|
||||||
|
@ -44,7 +45,7 @@ export class Sponsors extends Component<any, any> {
|
||||||
<h5>{i18n.t('donate_to_lemmy')}</h5>
|
<h5>{i18n.t('donate_to_lemmy')}</h5>
|
||||||
<p>
|
<p>
|
||||||
<T i18nKey="sponsor_message">
|
<T i18nKey="sponsor_message">
|
||||||
#<a href="https://github.com/dessalines/lemmy">#</a>
|
#<a href={repoUrl}>#</a>
|
||||||
</T>
|
</T>
|
||||||
</p>
|
</p>
|
||||||
<a class="btn btn-secondary" href="https://liberapay.com/Lemmy/">
|
<a class="btn btn-secondary" href="https://liberapay.com/Lemmy/">
|
||||||
|
@ -56,6 +57,12 @@ export class Sponsors extends Component<any, any> {
|
||||||
>
|
>
|
||||||
{i18n.t('support_on_patreon')}
|
{i18n.t('support_on_patreon')}
|
||||||
</a>
|
</a>
|
||||||
|
<a
|
||||||
|
class="btn btn-secondary ml-2"
|
||||||
|
href="https://opencollective.com/lemmy"
|
||||||
|
>
|
||||||
|
{i18n.t('support_on_open_collective')}
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
9
ui/src/components/symbols.tsx
vendored
9
ui/src/components/symbols.tsx
vendored
File diff suppressed because one or more lines are too long
36
ui/src/components/user-listing.tsx
vendored
Normal file
36
ui/src/components/user-listing.tsx
vendored
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import { Component } from 'inferno';
|
||||||
|
import { Link } from 'inferno-router';
|
||||||
|
import { UserView } from '../interfaces';
|
||||||
|
import { pictshareAvatarThumbnail, showAvatars } from '../utils';
|
||||||
|
|
||||||
|
interface UserOther {
|
||||||
|
name: string;
|
||||||
|
avatar?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UserListingProps {
|
||||||
|
user: UserView | UserOther;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UserListing extends Component<UserListingProps, any> {
|
||||||
|
constructor(props: any, context: any) {
|
||||||
|
super(props, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let user = this.props.user;
|
||||||
|
return (
|
||||||
|
<Link className="text-body font-weight-bold" to={`/u/${user.name}`}>
|
||||||
|
{user.avatar && showAvatars() && (
|
||||||
|
<img
|
||||||
|
height="32"
|
||||||
|
width="32"
|
||||||
|
src={pictshareAvatarThumbnail(user.avatar)}
|
||||||
|
class="rounded-circle mr-2"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<span>{user.name}</span>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
18
ui/src/index.tsx
vendored
18
ui/src/index.tsx
vendored
|
@ -15,27 +15,25 @@ import { Communities } from './components/communities';
|
||||||
import { User } from './components/user';
|
import { User } from './components/user';
|
||||||
import { Modlog } from './components/modlog';
|
import { Modlog } from './components/modlog';
|
||||||
import { Setup } from './components/setup';
|
import { Setup } from './components/setup';
|
||||||
|
import { AdminSettings } from './components/admin-settings';
|
||||||
import { Inbox } from './components/inbox';
|
import { Inbox } from './components/inbox';
|
||||||
import { Search } from './components/search';
|
import { Search } from './components/search';
|
||||||
import { Sponsors } from './components/sponsors';
|
import { Sponsors } from './components/sponsors';
|
||||||
import { Symbols } from './components/symbols';
|
import { Symbols } from './components/symbols';
|
||||||
import { i18n } from './i18next';
|
import { i18n } from './i18next';
|
||||||
|
|
||||||
import { WebSocketService, UserService } from './services';
|
|
||||||
|
|
||||||
const container = document.getElementById('app');
|
const container = document.getElementById('app');
|
||||||
|
|
||||||
class Index extends Component<any, any> {
|
class Index extends Component<any, any> {
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
WebSocketService.Instance;
|
|
||||||
UserService.Instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Provider i18next={i18n}>
|
<Provider i18next={i18n}>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
|
<div>
|
||||||
<Navbar />
|
<Navbar />
|
||||||
<div class="mt-4 p-0 fl-1">
|
<div class="mt-4 p-0 fl-1">
|
||||||
<Switch>
|
<Switch>
|
||||||
|
@ -51,9 +49,15 @@ class Index extends Component<any, any> {
|
||||||
path={`/create_private_message`}
|
path={`/create_private_message`}
|
||||||
component={CreatePrivateMessage}
|
component={CreatePrivateMessage}
|
||||||
/>
|
/>
|
||||||
<Route path={`/communities/page/:page`} component={Communities} />
|
<Route
|
||||||
|
path={`/communities/page/:page`}
|
||||||
|
component={Communities}
|
||||||
|
/>
|
||||||
<Route path={`/communities`} component={Communities} />
|
<Route path={`/communities`} component={Communities} />
|
||||||
<Route path={`/post/:id/comment/:comment_id`} component={Post} />
|
<Route
|
||||||
|
path={`/post/:id/comment/:comment_id`}
|
||||||
|
component={Post}
|
||||||
|
/>
|
||||||
<Route path={`/post/:id`} component={Post} />
|
<Route path={`/post/:id`} component={Post} />
|
||||||
<Route
|
<Route
|
||||||
path={`/c/:name/data_type/:data_type/sort/:sort/page/:page`}
|
path={`/c/:name/data_type/:data_type/sort/:sort/page/:page`}
|
||||||
|
@ -74,6 +78,7 @@ class Index extends Component<any, any> {
|
||||||
/>
|
/>
|
||||||
<Route path={`/modlog`} component={Modlog} />
|
<Route path={`/modlog`} component={Modlog} />
|
||||||
<Route path={`/setup`} component={Setup} />
|
<Route path={`/setup`} component={Setup} />
|
||||||
|
<Route path={`/admin`} component={AdminSettings} />
|
||||||
<Route
|
<Route
|
||||||
path={`/search/q/:q/type/:type/sort/:sort/page/:page`}
|
path={`/search/q/:q/type/:type/sort/:sort/page/:page`}
|
||||||
component={Search}
|
component={Search}
|
||||||
|
@ -88,6 +93,7 @@ class Index extends Component<any, any> {
|
||||||
<Symbols />
|
<Symbols />
|
||||||
</div>
|
</div>
|
||||||
<Footer />
|
<Footer />
|
||||||
|
</div>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
</Provider>
|
</Provider>
|
||||||
);
|
);
|
||||||
|
|
21
ui/src/interfaces.ts
vendored
21
ui/src/interfaces.ts
vendored
|
@ -43,6 +43,8 @@ export enum UserOperation {
|
||||||
GetPrivateMessages,
|
GetPrivateMessages,
|
||||||
UserJoin,
|
UserJoin,
|
||||||
GetComments,
|
GetComments,
|
||||||
|
GetSiteConfig,
|
||||||
|
SaveSiteConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum CommentSortType {
|
export enum CommentSortType {
|
||||||
|
@ -724,6 +726,19 @@ export interface SiteForm {
|
||||||
auth?: string;
|
auth?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface GetSiteConfig {
|
||||||
|
auth?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GetSiteConfigResponse {
|
||||||
|
config_hjson: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SiteConfigForm {
|
||||||
|
config_hjson: string;
|
||||||
|
auth?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface GetSiteResponse {
|
export interface GetSiteResponse {
|
||||||
site: Site;
|
site: Site;
|
||||||
admins: Array<UserView>;
|
admins: Array<UserView>;
|
||||||
|
@ -871,7 +886,8 @@ export type MessageType =
|
||||||
| PasswordChangeForm
|
| PasswordChangeForm
|
||||||
| PrivateMessageForm
|
| PrivateMessageForm
|
||||||
| EditPrivateMessageForm
|
| EditPrivateMessageForm
|
||||||
| GetPrivateMessagesForm;
|
| GetPrivateMessagesForm
|
||||||
|
| SiteConfigForm;
|
||||||
|
|
||||||
type ResponseType =
|
type ResponseType =
|
||||||
| SiteResponse
|
| SiteResponse
|
||||||
|
@ -893,7 +909,8 @@ type ResponseType =
|
||||||
| BanUserResponse
|
| BanUserResponse
|
||||||
| AddAdminResponse
|
| AddAdminResponse
|
||||||
| PrivateMessageResponse
|
| PrivateMessageResponse
|
||||||
| PrivateMessagesResponse;
|
| PrivateMessagesResponse
|
||||||
|
| GetSiteConfigResponse;
|
||||||
|
|
||||||
export interface WebSocketResponse {
|
export interface WebSocketResponse {
|
||||||
op: UserOperation;
|
op: UserOperation;
|
||||||
|
|
13
ui/src/services/WebSocketService.ts
vendored
13
ui/src/services/WebSocketService.ts
vendored
|
@ -40,6 +40,8 @@ import {
|
||||||
GetPrivateMessagesForm,
|
GetPrivateMessagesForm,
|
||||||
GetCommentsForm,
|
GetCommentsForm,
|
||||||
UserJoinForm,
|
UserJoinForm,
|
||||||
|
GetSiteConfig,
|
||||||
|
SiteConfigForm,
|
||||||
MessageType,
|
MessageType,
|
||||||
WebSocketJsonResponse,
|
WebSocketJsonResponse,
|
||||||
} from '../interfaces';
|
} from '../interfaces';
|
||||||
|
@ -268,6 +270,12 @@ export class WebSocketService {
|
||||||
this.ws.send(this.wsSendWrapper(UserOperation.GetSite, {}));
|
this.ws.send(this.wsSendWrapper(UserOperation.GetSite, {}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getSiteConfig() {
|
||||||
|
let siteConfig: GetSiteConfig = {};
|
||||||
|
this.setAuth(siteConfig);
|
||||||
|
this.ws.send(this.wsSendWrapper(UserOperation.GetSiteConfig, siteConfig));
|
||||||
|
}
|
||||||
|
|
||||||
public search(form: SearchForm) {
|
public search(form: SearchForm) {
|
||||||
this.setAuth(form, false);
|
this.setAuth(form, false);
|
||||||
this.ws.send(this.wsSendWrapper(UserOperation.Search, form));
|
this.ws.send(this.wsSendWrapper(UserOperation.Search, form));
|
||||||
|
@ -314,6 +322,11 @@ export class WebSocketService {
|
||||||
this.ws.send(this.wsSendWrapper(UserOperation.GetPrivateMessages, form));
|
this.ws.send(this.wsSendWrapper(UserOperation.GetPrivateMessages, form));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public saveSiteConfig(form: SiteConfigForm) {
|
||||||
|
this.setAuth(form);
|
||||||
|
this.ws.send(this.wsSendWrapper(UserOperation.SaveSiteConfig, form));
|
||||||
|
}
|
||||||
|
|
||||||
private wsSendWrapper(op: UserOperation, data: MessageType) {
|
private wsSendWrapper(op: UserOperation, data: MessageType) {
|
||||||
let send = { op: UserOperation[op], data: data };
|
let send = { op: UserOperation[op], data: data };
|
||||||
console.log(send);
|
console.log(send);
|
||||||
|
|
24
ui/src/utils.ts
vendored
24
ui/src/utils.ts
vendored
|
@ -43,8 +43,9 @@ import twemoji from 'twemoji';
|
||||||
import emojiShortName from 'emoji-short-name';
|
import emojiShortName from 'emoji-short-name';
|
||||||
import Toastify from 'toastify-js';
|
import Toastify from 'toastify-js';
|
||||||
import tippy from 'tippy.js';
|
import tippy from 'tippy.js';
|
||||||
|
import EmojiButton from '@joeattardi/emoji-button';
|
||||||
|
|
||||||
export const repoUrl = 'https://github.com/dessalines/lemmy';
|
export const repoUrl = 'https://github.com/LemmyNet/lemmy';
|
||||||
export const helpGuideUrl = '/docs/about_guide.html';
|
export const helpGuideUrl = '/docs/about_guide.html';
|
||||||
export const markdownHelpUrl = `${helpGuideUrl}#markdown-guide`;
|
export const markdownHelpUrl = `${helpGuideUrl}#markdown-guide`;
|
||||||
export const sortingHelpUrl = `${helpGuideUrl}#sorting`;
|
export const sortingHelpUrl = `${helpGuideUrl}#sorting`;
|
||||||
|
@ -88,6 +89,14 @@ export const themes = [
|
||||||
'i386',
|
'i386',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const emojiPicker = new EmojiButton({
|
||||||
|
// Use the emojiShortName from native
|
||||||
|
style: 'twemoji',
|
||||||
|
theme: 'dark',
|
||||||
|
position: 'auto-start',
|
||||||
|
// TODO i18n
|
||||||
|
});
|
||||||
|
|
||||||
export function randomStr() {
|
export function randomStr() {
|
||||||
return Math.random()
|
return Math.random()
|
||||||
.toString(36)
|
.toString(36)
|
||||||
|
@ -473,8 +482,9 @@ export function setupTribute(): Tribute {
|
||||||
{
|
{
|
||||||
trigger: ':',
|
trigger: ':',
|
||||||
menuItemTemplate: (item: any) => {
|
menuItemTemplate: (item: any) => {
|
||||||
let emoji = `:${item.original.key}:`;
|
let shortName = `:${item.original.key}:`;
|
||||||
return `${item.original.val} ${emoji}`;
|
let twemojiIcon = twemoji.parse(item.original.val);
|
||||||
|
return `${twemojiIcon} ${shortName}`;
|
||||||
},
|
},
|
||||||
selectTemplate: (item: any) => {
|
selectTemplate: (item: any) => {
|
||||||
return `:${item.original.key}:`;
|
return `:${item.original.key}:`;
|
||||||
|
@ -824,6 +834,14 @@ function randomHsl() {
|
||||||
return `hsla(${Math.random() * 360}, 100%, 50%, 1)`;
|
return `hsla(${Math.random() * 360}, 100%, 50%, 1)`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function previewLines(text: string, lines: number = 3): string {
|
||||||
|
// Use lines * 2 because markdown requires 2 lines
|
||||||
|
return text
|
||||||
|
.split('\n')
|
||||||
|
.slice(0, lines * 2)
|
||||||
|
.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
export function hostname(url: string): string {
|
export function hostname(url: string): string {
|
||||||
return new URL(url).hostname;
|
return new URL(url).hostname;
|
||||||
}
|
}
|
||||||
|
|
2
ui/src/version.ts
vendored
2
ui/src/version.ts
vendored
|
@ -1 +1 @@
|
||||||
export const version: string = 'v0.6.44';
|
export const version: string = 'v0.6.49';
|
||||||
|
|
6
ui/translations/en.json
vendored
6
ui/translations/en.json
vendored
|
@ -53,6 +53,8 @@
|
||||||
"mods": "mods",
|
"mods": "mods",
|
||||||
"moderates": "Moderates",
|
"moderates": "Moderates",
|
||||||
"settings": "Settings",
|
"settings": "Settings",
|
||||||
|
"admin_settings": "Admin Settings",
|
||||||
|
"site_config": "Site Configuration",
|
||||||
"remove_as_mod": "remove as mod",
|
"remove_as_mod": "remove as mod",
|
||||||
"appoint_as_mod": "appoint as mod",
|
"appoint_as_mod": "appoint as mod",
|
||||||
"modlog": "Modlog",
|
"modlog": "Modlog",
|
||||||
|
@ -78,6 +80,7 @@
|
||||||
"unban": "unban",
|
"unban": "unban",
|
||||||
"unban_from_site": "unban from site",
|
"unban_from_site": "unban from site",
|
||||||
"banned": "banned",
|
"banned": "banned",
|
||||||
|
"banned_users": "Banned Users",
|
||||||
"save": "save",
|
"save": "save",
|
||||||
"unsave": "unsave",
|
"unsave": "unsave",
|
||||||
"create": "create",
|
"create": "create",
|
||||||
|
@ -187,6 +190,7 @@
|
||||||
"Lemmy is free, <1>open-source</1> 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:",
|
"Lemmy is free, <1>open-source</1> software, meaning no advertising, monetizing, or venture capital, ever. Your donations directly support full-time development of the project. Thank you to the following people:",
|
||||||
"support_on_patreon": "Support on Patreon",
|
"support_on_patreon": "Support on Patreon",
|
||||||
"support_on_liberapay": "Support on Liberapay",
|
"support_on_liberapay": "Support on Liberapay",
|
||||||
|
"support_on_open_collective": "Support on OpenCollective",
|
||||||
"donate_to_lemmy": "Donate to Lemmy",
|
"donate_to_lemmy": "Donate to Lemmy",
|
||||||
"donate": "Donate",
|
"donate": "Donate",
|
||||||
"general_sponsors":
|
"general_sponsors":
|
||||||
|
@ -210,6 +214,7 @@
|
||||||
"Lemmy is a <1>link aggregator</1> / reddit alternative, intended to work in the <2>fediverse</2>.<3></3>It's self-hostable, has live-updating comment threads, and is tiny (<4>~80kB</4>). Federation into the ActivityPub network is on the roadmap. <5></5>This is a <6>very early beta version</6>, and a lot of features are currently broken or missing. <7></7>Suggest new features or report bugs <8>here.</8><9></9>Made with <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.",
|
"Lemmy is a <1>link aggregator</1> / reddit alternative, intended to work in the <2>fediverse</2>.<3></3>It's self-hostable, has live-updating comment threads, and is tiny (<4>~80kB</4>). Federation into the ActivityPub network is on the roadmap. <5></5>This is a <6>very early beta version</6>, and a lot of features are currently broken or missing. <7></7>Suggest new features or report bugs <8>here.</8><9></9>Made with <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.",
|
||||||
"not_logged_in": "Not logged in.",
|
"not_logged_in": "Not logged in.",
|
||||||
"logged_in": "Logged in.",
|
"logged_in": "Logged in.",
|
||||||
|
"site_saved": "Site Saved.",
|
||||||
"community_ban": "You have been banned from this community.",
|
"community_ban": "You have been banned from this community.",
|
||||||
"site_ban": "You have been banned from the site",
|
"site_ban": "You have been banned from the site",
|
||||||
"couldnt_create_comment": "Couldn't create comment.",
|
"couldnt_create_comment": "Couldn't create comment.",
|
||||||
|
@ -251,5 +256,6 @@
|
||||||
"couldnt_update_private_message": "Couldn't update private message.",
|
"couldnt_update_private_message": "Couldn't update private message.",
|
||||||
"time": "Time",
|
"time": "Time",
|
||||||
"action": "Action",
|
"action": "Action",
|
||||||
|
"emoji_picker": "Emoji Picker",
|
||||||
"block_leaving": "Are you sure you want to leave?"
|
"block_leaving": "Are you sure you want to leave?"
|
||||||
}
|
}
|
||||||
|
|
72
ui/translations/es.json
vendored
72
ui/translations/es.json
vendored
|
@ -4,13 +4,15 @@
|
||||||
"no_posts": "Sin publicaciones.",
|
"no_posts": "Sin publicaciones.",
|
||||||
"create_a_post": "Crear una publicación",
|
"create_a_post": "Crear una publicación",
|
||||||
"create_post": "Crear Publicación",
|
"create_post": "Crear Publicación",
|
||||||
"number_of_posts": "{{count}} Publicaciones",
|
"number_of_posts": "{{count}} Publicación",
|
||||||
|
"number_of_posts_plural": "{{count}} Publicaciónes",
|
||||||
"posts": "Publicaciones",
|
"posts": "Publicaciones",
|
||||||
"related_posts": "Estas publicaciones podrían estar relacionadas",
|
"related_posts": "Estas publicaciones podrían estar relacionadas",
|
||||||
"cross_posts": "Este link también ha sido publicado en:",
|
"cross_posts": "Este link también ha sido publicado en:",
|
||||||
"cross_post": "cross-post",
|
"cross_post": "cross-post",
|
||||||
"comments": "Comentarios",
|
"comments": "Comentarios",
|
||||||
"number_of_comments": "{{count}} Comentarios",
|
"number_of_comments": "{{count}} Comentario",
|
||||||
|
"number_of_comments_plural": "{{count}} Comentarios",
|
||||||
"remove_comment": "Eliminar Comentarios",
|
"remove_comment": "Eliminar Comentarios",
|
||||||
"communities": "Comunidades",
|
"communities": "Comunidades",
|
||||||
"users": "Usuarios",
|
"users": "Usuarios",
|
||||||
|
@ -20,7 +22,8 @@
|
||||||
"subscribed_to_communities": "Suscrito a <1>comunidades</1>",
|
"subscribed_to_communities": "Suscrito a <1>comunidades</1>",
|
||||||
"trending_communities": "<1>Comunidades</1> en tendencia",
|
"trending_communities": "<1>Comunidades</1> en tendencia",
|
||||||
"list_of_communities": "Lista de comunidades",
|
"list_of_communities": "Lista de comunidades",
|
||||||
"number_of_communities": "{{count}} Comunidades",
|
"number_of_communities": "{{count}} Comunidad",
|
||||||
|
"number_of_communities_plural": "{{count}} Comunidades",
|
||||||
"community_reqs": "minúsculas, guión bajo, y sin espacios.",
|
"community_reqs": "minúsculas, guión bajo, y sin espacios.",
|
||||||
"create_private_message": "Crear Mensaje Privado",
|
"create_private_message": "Crear Mensaje Privado",
|
||||||
"send_secure_message": "Enviar Mensaje Seguro",
|
"send_secure_message": "Enviar Mensaje Seguro",
|
||||||
|
@ -63,8 +66,7 @@
|
||||||
"delete": "eliminar",
|
"delete": "eliminar",
|
||||||
"deleted": "eliminado",
|
"deleted": "eliminado",
|
||||||
"delete_account": "Eliminar Cuenta",
|
"delete_account": "Eliminar Cuenta",
|
||||||
"delete_account_confirm":
|
"delete_account_confirm": "Aviso: esta acción eliminará permanentemente tu información. Introduce tu contraseña para continuar",
|
||||||
"Aviso: esta acción eliminará permanentemente tu información. Introduce tu contraseña para continuar",
|
|
||||||
"restore": "restaurar",
|
"restore": "restaurar",
|
||||||
"ban": "expulsar",
|
"ban": "expulsar",
|
||||||
"ban_from_site": "expulsar del sitio",
|
"ban_from_site": "expulsar del sitio",
|
||||||
|
@ -77,10 +79,14 @@
|
||||||
"creator": "creador",
|
"creator": "creador",
|
||||||
"username": "Nombre de Usuario",
|
"username": "Nombre de Usuario",
|
||||||
"email_or_username": "Correo o Usuario",
|
"email_or_username": "Correo o Usuario",
|
||||||
"number_of_users": "{{count}} Usuarios",
|
"number_of_users": "{{count}} Usuario",
|
||||||
"number_of_subscribers": "{{count}} Suscriptores",
|
"number_of_users_plural": "{{count}} Usuarios",
|
||||||
"number_of_points": "{{count}} Puntos",
|
"number_of_subscribers": "{{count}} Suscriptor",
|
||||||
"number_online": "{{count}} Usuarios En Línea",
|
"number_of_subscribers_plural": "{{count}} Suscriptores",
|
||||||
|
"number_of_points": "{{count}} Punto",
|
||||||
|
"number_of_points_plural": "{{count}} Puntos",
|
||||||
|
"number_online": "{{count}} Usuario En Línea",
|
||||||
|
"number_online_plural": "{{count}} Usuarios En Línea",
|
||||||
"name": "Nombre",
|
"name": "Nombre",
|
||||||
"title": "Titulo",
|
"title": "Titulo",
|
||||||
"category": "Categoría",
|
"category": "Categoría",
|
||||||
|
@ -120,8 +126,7 @@
|
||||||
"login_sign_up": "Iniciar sesión / Crear cuenta",
|
"login_sign_up": "Iniciar sesión / Crear cuenta",
|
||||||
"login": "Iniciar sesión",
|
"login": "Iniciar sesión",
|
||||||
"sign_up": "Crear cuenta",
|
"sign_up": "Crear cuenta",
|
||||||
"notifications_error":
|
"notifications_error": "Notificaciones de escritorio no disponibles en tu navegador. Prueba Firefox o Chrome.",
|
||||||
"Notificaciones de escritorio no disponibles en tu navegador. Prueba Firefox o Chrome.",
|
|
||||||
"unread_messages": "Mensajes no leídos",
|
"unread_messages": "Mensajes no leídos",
|
||||||
"messages": "Mensajes",
|
"messages": "Mensajes",
|
||||||
"password": "Contraseña",
|
"password": "Contraseña",
|
||||||
|
@ -134,8 +139,7 @@
|
||||||
"no_email_setup": "Este servidor no ha activado correctamente el correo.",
|
"no_email_setup": "Este servidor no ha activado correctamente el correo.",
|
||||||
"email": "Correo electrónico",
|
"email": "Correo electrónico",
|
||||||
"matrix_user_id": "Usuario Matricial",
|
"matrix_user_id": "Usuario Matricial",
|
||||||
"private_message_disclaimer":
|
"private_message_disclaimer": "Aviso: Los mensajes privados en Lemmy no son seguros. Por favor cree una cuenta en <1>Riot.im</1> para mensajeria segura.",
|
||||||
"Aviso: Los mensajes privados en Lemmy no son seguros. Por favor cree una cuenta en <1>Riot.im</1> para mensajeria segura.",
|
|
||||||
"send_notifications_to_email": "Enviar notificaciones al correo",
|
"send_notifications_to_email": "Enviar notificaciones al correo",
|
||||||
"optional": "Opcional",
|
"optional": "Opcional",
|
||||||
"expires": "Expira",
|
"expires": "Expira",
|
||||||
|
@ -165,14 +169,12 @@
|
||||||
"theme": "Tema",
|
"theme": "Tema",
|
||||||
"sponsors": "Patrocinadores",
|
"sponsors": "Patrocinadores",
|
||||||
"sponsors_of_lemmy": "Patrocinadores de Lemmy",
|
"sponsors_of_lemmy": "Patrocinadores de Lemmy",
|
||||||
"sponsor_message":
|
"sponsor_message": "Lemmy es software libre y de <1>código abierto</1>, 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:",
|
||||||
"Lemmy es software libre y de <1>código abierto</1>, 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_patreon": "Apoyo en Patreon",
|
||||||
"support_on_liberapay": "Apoyo en Liberapay",
|
"support_on_liberapay": "Apoyo en Liberapay",
|
||||||
"donate_to_lemmy": "Donar a Lemmy",
|
"donate_to_lemmy": "Donar a Lemmy",
|
||||||
"donate": "Donar",
|
"donate": "Donar",
|
||||||
"general_sponsors":
|
"general_sponsors": "Los Patrocinadores Generales son aquellos que señaron entre $10 y $39 a Lemmy.",
|
||||||
"Los Patrocinadores Generales son aquellos que señaron entre $10 y $39 a Lemmy.",
|
|
||||||
"crypto": "Crypto",
|
"crypto": "Crypto",
|
||||||
"bitcoin": "Bitcoin",
|
"bitcoin": "Bitcoin",
|
||||||
"ethereum": "Ethereum",
|
"ethereum": "Ethereum",
|
||||||
|
@ -188,8 +190,7 @@
|
||||||
"yes": "sí",
|
"yes": "sí",
|
||||||
"no": "no",
|
"no": "no",
|
||||||
"powered_by": "Impulsado por",
|
"powered_by": "Impulsado por",
|
||||||
"landing_0":
|
"landing_0": "Lemmy es un <1>agregador de links</1> / alternativa a reddit, con la intención de funcionar en el <2>fediverso</2>.<3></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</4>). Federar con el sistema de redes ActivityPub forma parte de los objetivos del proyecto. <5></5>Esta es una <6>version beta muy prematura</6>, y actualmente muchas de las características están rotas o faltan. <7></7>Sugiere nuevas características o reporta errores <8>aquí</8>.<9></9>Hecho con <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.",
|
||||||
"Lemmy es un <1>agregador de links</1> / alternativa a reddit, con la intención de funcionar en el <2>fediverso</2>.<3></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</4>). Federar con el sistema de redes ActivityPub forma parte de los objetivos del proyecto. <5></5>Esta es una <6>version beta muy prematura</6>, y actualmente muchas de las características están rotas o faltan. <7></7>Sugiere nuevas características o reporta errores <8>aquí</8>.<9></9>Hecho con <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.",
|
|
||||||
"not_logged_in": "No has iniciado sesión.",
|
"not_logged_in": "No has iniciado sesión.",
|
||||||
"logged_in": "Has iniciado sesión.",
|
"logged_in": "Has iniciado sesión.",
|
||||||
"community_ban": "Has sido expulsado de esta comunidad.",
|
"community_ban": "Has sido expulsado de esta comunidad.",
|
||||||
|
@ -204,12 +205,9 @@
|
||||||
"couldnt_find_community": "No se pudo encontrar la comunidad.",
|
"couldnt_find_community": "No se pudo encontrar la comunidad.",
|
||||||
"couldnt_update_community": "No se pudo actualizar la comunidad.",
|
"couldnt_update_community": "No se pudo actualizar la comunidad.",
|
||||||
"community_already_exists": "Esta comunidad ya existe.",
|
"community_already_exists": "Esta comunidad ya existe.",
|
||||||
"community_moderator_already_exists":
|
"community_moderator_already_exists": "Este moderador de la comunidad ya existe.",
|
||||||
"Este moderador de la comunidad ya existe.",
|
"community_follower_already_exists": "Este seguidor de la comunidad ya existe.",
|
||||||
"community_follower_already_exists":
|
"community_user_already_banned": "Este usuario de la comunidad ya fue expulsado.",
|
||||||
"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_create_post": "No se pudo crear la publicación.",
|
||||||
"couldnt_like_post": "No se pudo gustar la publicación.",
|
"couldnt_like_post": "No se pudo gustar la publicación.",
|
||||||
"couldnt_find_post": "No se pudo encontrar la publicación.",
|
"couldnt_find_post": "No se pudo encontrar la publicación.",
|
||||||
|
@ -220,21 +218,31 @@
|
||||||
"not_an_admin": "No es un administrador.",
|
"not_an_admin": "No es un administrador.",
|
||||||
"site_already_exists": "El sitio ya existe.",
|
"site_already_exists": "El sitio ya existe.",
|
||||||
"couldnt_update_site": "No se pudo actualizar el sitio.",
|
"couldnt_update_site": "No se pudo actualizar el sitio.",
|
||||||
"couldnt_find_that_username_or_email":
|
"couldnt_find_that_username_or_email": "No se pudo encontrar ese nombre de usuario o correo electrónico.",
|
||||||
"No se pudo encontrar ese nombre de usuario o correo electrónico.",
|
|
||||||
"password_incorrect": "Contraseña incorrecta.",
|
"password_incorrect": "Contraseña incorrecta.",
|
||||||
"passwords_dont_match": "Las contraseñas no coinciden.",
|
"passwords_dont_match": "Las contraseñas no coinciden.",
|
||||||
"admin_already_created": "Lo sentimos, ya hay un adminisitrador.",
|
"admin_already_created": "Lo sentimos, ya hay un adminisitrador.",
|
||||||
"user_already_exists": "El usuario ya existe.",
|
"user_already_exists": "El usuario ya existe.",
|
||||||
"email_already_exists": "El correo ya está en uso.",
|
"email_already_exists": "El correo ya está en uso.",
|
||||||
"couldnt_update_user": "No se pudo actualizar el usuario.",
|
"couldnt_update_user": "No se pudo actualizar el usuario.",
|
||||||
"system_err_login":
|
"system_err_login": "Error del sistema. Intente cerrar sesión e ingresar de nuevo.",
|
||||||
"Error del sistema. Intente cerrar sesión e ingresar de nuevo.",
|
|
||||||
"couldnt_create_private_message": "No se pudo crear el mensaje privado.",
|
"couldnt_create_private_message": "No se pudo crear el mensaje privado.",
|
||||||
"no_private_message_edit_allowed":
|
"no_private_message_edit_allowed": "Sin permisos para editar el mensaje privado.",
|
||||||
"Sin permisos para editar el mensaje privado.",
|
|
||||||
"couldnt_update_private_message": "No se pudo actualizar el mensaje privado.",
|
"couldnt_update_private_message": "No se pudo actualizar el mensaje privado.",
|
||||||
"old": "Antiguo",
|
"old": "Antiguo",
|
||||||
"time": "Tiempo",
|
"time": "Tiempo",
|
||||||
"action": "Acción"
|
"action": "Acción",
|
||||||
|
"more": "más",
|
||||||
|
"cross_posted_to": "publicado también en:",
|
||||||
|
"sorting_help": "ayuda del orden",
|
||||||
|
"upvote": "Voto Positivo",
|
||||||
|
"number_of_upvotes": "{{count}} Voto Positivo",
|
||||||
|
"number_of_upvotes_plural": "{{count}} Votos Positivos",
|
||||||
|
"downvote": "Voto Negativo",
|
||||||
|
"number_of_downvotes": "{{count}} Voto Negativo",
|
||||||
|
"number_of_downvotes_plural": "{{count}} Votos Negativos",
|
||||||
|
"couldnt_get_comments": "No se pudo obtener los comentarios.",
|
||||||
|
"post_title_too_long": "El título de la publicación es muy largo.",
|
||||||
|
"block_leaving": "¿Está seguro de que desea salir?",
|
||||||
|
"show_context": "Mostrar contexto"
|
||||||
}
|
}
|
||||||
|
|
3
ui/translations/fr.json
vendored
3
ui/translations/fr.json
vendored
|
@ -227,5 +227,6 @@
|
||||||
"no_private_message_edit_allowed": "Pas autorisé à modifier un message privé.",
|
"no_private_message_edit_allowed": "Pas autorisé à modifier un message privé.",
|
||||||
"couldnt_update_private_message": "Impossible de modifier un message privé.",
|
"couldnt_update_private_message": "Impossible de modifier un message privé.",
|
||||||
"time": "Temps",
|
"time": "Temps",
|
||||||
"action": "Action"
|
"action": "Action",
|
||||||
|
"more": "plus"
|
||||||
}
|
}
|
||||||
|
|
223
ui/translations/ka.json
vendored
223
ui/translations/ka.json
vendored
|
@ -1,2 +1,225 @@
|
||||||
{
|
{
|
||||||
|
"post": "პოსტი",
|
||||||
|
"remove_post": "პოსტის წაშლა",
|
||||||
|
"no_posts": "0 პოსტები",
|
||||||
|
"create_a_post": "პოსტის შექმნა",
|
||||||
|
"create_post": "პოსტის შექმნა",
|
||||||
|
"number_of_posts": "თარგმნა",
|
||||||
|
"number_of_posts_plural": "თარგმნა",
|
||||||
|
"posts": "პოსტები",
|
||||||
|
"cross_posts": "ეს ლინკი უკვე დადებულია აქ:",
|
||||||
|
"comments": "კომენტარები",
|
||||||
|
"number_of_comments": "კომენტარი",
|
||||||
|
"number_of_comments_plural": "კომანტარები",
|
||||||
|
"remove_comment": "კომენტარის წაშლა",
|
||||||
|
"communities": "თემები",
|
||||||
|
"users": "მომხმარებელი",
|
||||||
|
"create_a_community": "ახალი თემის შექმნა",
|
||||||
|
"create_community": "თემის შექმნა",
|
||||||
|
"remove_community": "თემის წაშლა",
|
||||||
|
"community_reqs": "პატარა ასო, ქვედა ტირე, და გამოტოვების გარეშე.",
|
||||||
|
"create_private_message": "კერძო მესეჯის შექმნა",
|
||||||
|
"send_secure_message": "ინკრიპტული მესეჯის გაგზავნა",
|
||||||
|
"send_message": "მესეჯის გაგზავნა",
|
||||||
|
"message": "მესეჯი",
|
||||||
|
"edit": "რადექტირება",
|
||||||
|
"reply": "პასუხის გაცემა",
|
||||||
|
"more": "მეტი",
|
||||||
|
"cancel": "გაუქება",
|
||||||
|
"upload_image": "სურათის ატვირთვა",
|
||||||
|
"avatar": "ავატარი",
|
||||||
|
"upload_avatar": "ავატარის ატვირთვა",
|
||||||
|
"show_context": "კონტექსტის ნახვა",
|
||||||
|
"sorting_help": "სორტირების დახმარება",
|
||||||
|
"view_source": "view source",
|
||||||
|
"unlock": "გაღება",
|
||||||
|
"lock": "ჩაკეტვა",
|
||||||
|
"sticky": "sticky",
|
||||||
|
"link": "ლინკი",
|
||||||
|
"archive_link": "ლინკის არქივება",
|
||||||
|
"mod": "მოდერატორი",
|
||||||
|
"mods": "მოდერატორები",
|
||||||
|
"moderates": "მოდერატორს",
|
||||||
|
"settings": "პარამეტრები",
|
||||||
|
"appoint_as_mod": "დანიშნე როგორც მოდერატორი",
|
||||||
|
"modlog": "მოდ-ლოგი",
|
||||||
|
"admin": "ადმინი",
|
||||||
|
"admins": "ადმინები",
|
||||||
|
"appoint_as_admin": "დანიშნე როგორც ადმინი",
|
||||||
|
"remove": "მოხსნა",
|
||||||
|
"removed": "მოხსნილია",
|
||||||
|
"locked": "ჩაკეტილი",
|
||||||
|
"stickied": "დაწეპებული",
|
||||||
|
"reason": "მიზეზი",
|
||||||
|
"mark_as_read": "მონიშნე როგორც წაკითხული",
|
||||||
|
"mark_as_unread": "მონიშნე როგორც წაუკითხავი",
|
||||||
|
"delete": "წაშლა",
|
||||||
|
"deleted": "წაშლილია",
|
||||||
|
"delete_account": "ჩემი ანგარიშის წაშლა",
|
||||||
|
"restore": "რასტორაცია",
|
||||||
|
"ban": "გაშავება",
|
||||||
|
"ban_from_site": "გაშავება საიტიდან",
|
||||||
|
"unban": "გაშავების გაუქმნება",
|
||||||
|
"unban_from_site": "სატიდან გაშავების გაუქმნება",
|
||||||
|
"banned": "გაშავებულია",
|
||||||
|
"save": "დამახსოვრება",
|
||||||
|
"unsave": "დამახსოვრების გაუქმნება",
|
||||||
|
"create": "შექმნა",
|
||||||
|
"preview": "წინასწარ ნახვა",
|
||||||
|
"show_avatars": "ავატარები გამოჩენა",
|
||||||
|
"formatting_help": "formatting help",
|
||||||
|
"unsticky": "unsticky",
|
||||||
|
"remove_as_mod": "მოხსენი როგორც მოდერატორი",
|
||||||
|
"remove_as_admin": "მოხსენი როგორც ადმინი",
|
||||||
|
"delete_account_confirm": "გაფთხილება: ეს შენს ყველაფერს წაშლის. პაროლი ჩაწერეთ რომ დაადასტუროთ.",
|
||||||
|
"creator": "შემქნელი",
|
||||||
|
"username": "მომხმარებლის სახელი",
|
||||||
|
"email_or_username": "ელ-პოსტა ან მომხმარებლის სახელი",
|
||||||
|
"number_of_users": "მომხმარებელი",
|
||||||
|
"number_of_users_plural": "მომხმარებლები",
|
||||||
|
"number_of_subscribers": "გამომწერი",
|
||||||
|
"number_of_subscribers_plural": "გამომწერები",
|
||||||
|
"number_of_points": "ქულა",
|
||||||
|
"number_of_points_plural": "ქულა",
|
||||||
|
"number_online": "მომხმარებელი საიტზე",
|
||||||
|
"number_online_plural": "მომხმარებელი საიტზე",
|
||||||
|
"name": "სახელი",
|
||||||
|
"title": "სათაური",
|
||||||
|
"category": "კატეგორია",
|
||||||
|
"subscribers": "გამომწერი",
|
||||||
|
"both": "ორივე",
|
||||||
|
"saved": "შანახული",
|
||||||
|
"unsubscribe": "გამოწერის გაუქმნება",
|
||||||
|
"subscribe": "გამოწერა",
|
||||||
|
"subscribed": "გამოწერილია",
|
||||||
|
"prev": "წუნა",
|
||||||
|
"next": "შემდეგი",
|
||||||
|
"sidebar": "Sidebar",
|
||||||
|
"sort_type": "სორტირების ტიპი",
|
||||||
|
"inbox": "Inbox",
|
||||||
|
"inbox_for": "<1>{{user}}</1>-s Inbox",
|
||||||
|
"mark_all_as_read": "მონიშვნა ყველასი როგორც წაკითხული",
|
||||||
|
"type": "ტიპი",
|
||||||
|
"unread": "წაუკითხავია",
|
||||||
|
"mentions": "ხსენებები",
|
||||||
|
"reply_sent": "პასუხი გაგზავნილია",
|
||||||
|
"message_sent": "მესეჯი",
|
||||||
|
"search": "ძებმა",
|
||||||
|
"overview": "გადახედვა",
|
||||||
|
"view": "ნახვა",
|
||||||
|
"logout": "გასვლა",
|
||||||
|
"login_sign_up": "შესვლა ან რეგისტრაცია",
|
||||||
|
"login": "შესვლა",
|
||||||
|
"sign_up": "რეგისტრაცია",
|
||||||
|
"unread_messages": "წაუკითხავი მესეჯები",
|
||||||
|
"messages": "მესეჯები",
|
||||||
|
"password": "პაროლი",
|
||||||
|
"verify_password": "პაროლის დადასტურება",
|
||||||
|
"old_password": "ძველი პაროლი",
|
||||||
|
"forgot_password": "აღგდენა",
|
||||||
|
"reset_password_mail_sent": "ელ-პოსტა შეამოწმეთ",
|
||||||
|
"password_change": "პაროლის შეცვლა",
|
||||||
|
"new_password": "ახალი პაროლი",
|
||||||
|
"email": "ელ-პოსტა",
|
||||||
|
"matrix_user_id": "მატრიცული მომხმარებელი",
|
||||||
|
"private_message_disclaimer": ".",
|
||||||
|
"send_notifications_to_email": "შეტყობინების გაგზავნა ელ-პოსტაზე",
|
||||||
|
"optional": "არასავალდებულო",
|
||||||
|
"expires": "ვადა გასდის",
|
||||||
|
"language": "ენა",
|
||||||
|
"browser_default": "Browser Default",
|
||||||
|
"enable_downvotes": "არმოწონების ჩართვა",
|
||||||
|
"upvote": "მოწონება",
|
||||||
|
"downvote": "არ მოწონება",
|
||||||
|
"open_registration": "რეგისტრაციის გახსნა",
|
||||||
|
"registration_closed": "რეგისტრაცია დახურულია",
|
||||||
|
"enable_nsfw": "Enable NSFW",
|
||||||
|
"url": "მისამართი",
|
||||||
|
"body": "ტექსტი",
|
||||||
|
"copy_suggested_title": "დაკოპირება რეკომინდებულის სათაური: {{title}}",
|
||||||
|
"community": "თემა",
|
||||||
|
"expand_here": "Expand here",
|
||||||
|
"subscribe_to_communities": "Subscribe to some <1>communities</1>.",
|
||||||
|
"chat": "ჩეტი",
|
||||||
|
"recent_comments": "ბოლო კომენტარები",
|
||||||
|
"no_results": "0 შედეგი",
|
||||||
|
"setup": "Setup",
|
||||||
|
"lemmy_instance_setup": "Lemmy Instance Setup",
|
||||||
|
"setup_admin": "Set Up Site Administrator",
|
||||||
|
"your_site": "შენი გვერდი",
|
||||||
|
"modified": "რედაკტირებული",
|
||||||
|
"nsfw": "NSFW",
|
||||||
|
"notifications_error": "გთხოვთ იხმაღეთ Chome ან Firefox შეტყობინებისთვის",
|
||||||
|
"no_email_setup": "This server hasn't correctly set up email.",
|
||||||
|
"downvotes_disabled": "არმოწონები გამორთულია",
|
||||||
|
"number_of_upvotes": "მოწონება",
|
||||||
|
"number_of_upvotes_plural": "მოწონება",
|
||||||
|
"number_of_downvotes": "არ მოწონება",
|
||||||
|
"number_of_downvotes_plural": "არ მოწონება",
|
||||||
|
"hot": "ცხელი",
|
||||||
|
"new": "ახალი",
|
||||||
|
"old": "ძველი",
|
||||||
|
"top_day": "ტოპ დღეს",
|
||||||
|
"week": "კვირა",
|
||||||
|
"month": "თვე",
|
||||||
|
"year": "წელი",
|
||||||
|
"all": "ყველა",
|
||||||
|
"top": "ტოპ",
|
||||||
|
"api": "API",
|
||||||
|
"show_nsfw": "Show NSFW content",
|
||||||
|
"related_posts": "ეს პოსტები შეიძლება ერთმანეც ეხება",
|
||||||
|
"cross_post": "გადაკვეთა-პოსტი",
|
||||||
|
"general_sponsors": "General Sponsors are those that pledged $10 to $39 to Lemmy.",
|
||||||
|
"cross_posted_to": "გადაკვეთა-პოსტი გაკეთდა: ",
|
||||||
|
"subscribed_to_communities": "მიყვები <1>communities</1>",
|
||||||
|
"trending_communities": "ტრენდული <1>communities</1>",
|
||||||
|
"list_of_communities": "ყველა თემა",
|
||||||
|
"number_of_communities": "თემა",
|
||||||
|
"number_of_communities_plural": "თემები",
|
||||||
|
"landing": "Lemmy is a <1>link aggregator</1> / reddit alternative, intended to work in the <2>fediverse</2>.<3></3>It's self-hostable, has live-updating comment threads, and is tiny (<4>~80kB</4>). Federation into the ActivityPub network is on the roadmap. <5></5>This is a <6>very early beta version</6>, and a lot of features are currently broken or missing. <7></7>Suggest new features or report bugs <8>here.</8><9></9>Made with <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.",
|
||||||
|
"docs": "დოკუმენტაცია",
|
||||||
|
"couldnt_like_comment": "კომენტარის მოწონება ვერ მოხერხდა.",
|
||||||
|
"couldnt_update_comment": "კომენტარის განახლება ვერ მოხერხდა.",
|
||||||
|
"replies": "პასუხები",
|
||||||
|
"theme": "საიტის თემა",
|
||||||
|
"sponsors": "სპონსორები",
|
||||||
|
"sponsors_of_lemmy": "Sponsors",
|
||||||
|
"sponsor_message": "Lemmy is free, <1>open-source</1> 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",
|
||||||
|
"no_comment_edit_allowed": "კომენტარის რედაკტირება არ შეიძლება.",
|
||||||
|
"donate": "Donate",
|
||||||
|
"crypto": "Crypto",
|
||||||
|
"bitcoin": "Bitcoin",
|
||||||
|
"ethereum": "Ethereum",
|
||||||
|
"code": "კოდი",
|
||||||
|
"joined": "დაემატა",
|
||||||
|
"by": "by",
|
||||||
|
"to": "to",
|
||||||
|
"from": "from",
|
||||||
|
"transfer_community": "transfer community",
|
||||||
|
"transfer_site": "transfer site",
|
||||||
|
"are_you_sure": "დარწმუნებული ხარ?",
|
||||||
|
"yes": "კი",
|
||||||
|
"no": "არა",
|
||||||
|
"powered_by": "Powered by",
|
||||||
|
"not_logged_in": "შასული არ ხართ",
|
||||||
|
"logged_in": "შაული ხართ.",
|
||||||
|
"community_ban": "შენ ამ თემისგან გაშავებული ხარ.",
|
||||||
|
"site_ban": "საიტიდან გაშავებული ხარ.",
|
||||||
|
"couldnt_create_comment": "კომენტარის შექმნა ვერ მოხერხდა.",
|
||||||
|
"couldnt_find_community": "ტემა არ მოიძებნა.",
|
||||||
|
"couldnt_save_comment": "კომენტარის შენახვა ვერ მოხერხდა.",
|
||||||
|
"couldnt_get_comments": "კომენტარების ნახვა ვერ მოხერხდა.",
|
||||||
|
"no_post_edit_allowed": "პოსტის რედაკტირება არ შეიძლება.",
|
||||||
|
"no_community_edit_allowed": "თემის რედაკტირება არ შეიძლება.",
|
||||||
|
"couldnt_update_community": "თემა ვერ განახლდა.",
|
||||||
|
"community_already_exists": "ეს თემა უკვე არსებობს.",
|
||||||
|
"community_follower_already_exists": "თემის ფოლოვორი უკვე არსებობს.",
|
||||||
|
"community_user_already_banned": "თემის მომხმარებელი უკვე შავ სიაშია.",
|
||||||
|
"couldnt_like_post": "პოსტის მოწონება ვერ მოხერხდა.",
|
||||||
|
"community_moderator_already_exists": "ამ თემის მოდერატორი უკვე არსებობს.",
|
||||||
|
"couldnt_create_post": "პოსტი ვერ შეიქმნა.",
|
||||||
|
"post_title_too_long": "პოსტის სათაური ძალიან გრძელია."
|
||||||
}
|
}
|
||||||
|
|
251
ui/yarn.lock
vendored
251
ui/yarn.lock
vendored
|
@ -126,10 +126,55 @@
|
||||||
lodash "^4.17.13"
|
lodash "^4.17.13"
|
||||||
to-fast-properties "^2.0.0"
|
to-fast-properties "^2.0.0"
|
||||||
|
|
||||||
"@popperjs/core@^2.1.1":
|
"@fortawesome/fontawesome-common-types@^0.2.28":
|
||||||
version "2.1.1"
|
version "0.2.28"
|
||||||
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.1.1.tgz#12c572ab88ef7345b43f21883fca26631c223085"
|
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.28.tgz#1091bdfe63b3f139441e9cba27aa022bff97d8b2"
|
||||||
integrity sha512-sLqWxCzC5/QHLhziXSCAksBxHfOnQlhPRVgPK0egEw+ktWvG75T2k+aYWVjVh9+WKeT3tlG3ZNbZQvZLmfuOIw==
|
integrity sha512-gtis2/5yLdfI6n0ia0jH7NJs5i/Z/8M/ZbQL6jXQhCthEOe5Cr5NcQPhgTvFxNOtURE03/ZqUcEskdn2M+QaBg==
|
||||||
|
|
||||||
|
"@fortawesome/fontawesome-svg-core@^1.2.22":
|
||||||
|
version "1.2.28"
|
||||||
|
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.28.tgz#e5b8c8814ef375f01f5d7c132d3c3a2f83a3abf9"
|
||||||
|
integrity sha512-4LeaNHWvrneoU0i8b5RTOJHKx7E+y7jYejplR7uSVB34+mp3Veg7cbKk7NBCLiI4TyoWS1wh9ZdoyLJR8wSAdg==
|
||||||
|
dependencies:
|
||||||
|
"@fortawesome/fontawesome-common-types" "^0.2.28"
|
||||||
|
|
||||||
|
"@fortawesome/free-regular-svg-icons@^5.10.2":
|
||||||
|
version "5.13.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.13.0.tgz#925a13d8bdda0678f71551828cac80ab47b8150c"
|
||||||
|
integrity sha512-70FAyiS5j+ANYD4dh9NGowTorNDnyvQHHpCM7FpnF7GxtDjBUCKdrFqCPzesEIpNDFNd+La3vex+jDk4nnUfpA==
|
||||||
|
dependencies:
|
||||||
|
"@fortawesome/fontawesome-common-types" "^0.2.28"
|
||||||
|
|
||||||
|
"@fortawesome/free-solid-svg-icons@^5.10.2":
|
||||||
|
version "5.13.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.13.0.tgz#44d9118668ad96b4fd5c9434a43efc5903525739"
|
||||||
|
integrity sha512-IHUgDJdomv6YtG4p3zl1B5wWf9ffinHIvebqQOmV3U+3SLw4fC+LUCCgwfETkbTtjy5/Qws2VoVf6z/ETQpFpg==
|
||||||
|
dependencies:
|
||||||
|
"@fortawesome/fontawesome-common-types" "^0.2.28"
|
||||||
|
|
||||||
|
"@joeattardi/emoji-button@^2.12.1":
|
||||||
|
version "2.12.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@joeattardi/emoji-button/-/emoji-button-2.12.1.tgz#190df7c00721e04742ed6f8852db828798a4cf98"
|
||||||
|
integrity sha512-rUuCXIcv4mRFK2IUKarYJN6J667wtH234smb1aQILzRf3/ycOoa6yUwnnvjxZeXMsPhuTnz15ndMOP2DhO5nNw==
|
||||||
|
dependencies:
|
||||||
|
"@fortawesome/fontawesome-svg-core" "^1.2.22"
|
||||||
|
"@fortawesome/free-regular-svg-icons" "^5.10.2"
|
||||||
|
"@fortawesome/free-solid-svg-icons" "^5.10.2"
|
||||||
|
"@popperjs/core" "^2.0.0"
|
||||||
|
focus-trap "^5.1.0"
|
||||||
|
tiny-emitter "^2.1.0"
|
||||||
|
tslib "^1.10.0"
|
||||||
|
twemoji "^12.1.5"
|
||||||
|
|
||||||
|
"@popperjs/core@^2.0.0":
|
||||||
|
version "2.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.2.3.tgz#0ae22b5650ab0b8fe508047245b66e71fc59e983"
|
||||||
|
integrity sha512-68EQPzEZRrpFavFX40V2+80eqzQIhgza2AGTXW+i8laxSA4It+Y13rmZInrAYoIujp8YO7YJPbvgOesDZcIulQ==
|
||||||
|
|
||||||
|
"@popperjs/core@^2.2.0":
|
||||||
|
version "2.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.3.2.tgz#1e56eb99bccddbda6a3e29aa4f3660f5b23edc43"
|
||||||
|
integrity sha512-18Tz3QghwsuHUC4gTNoxcEw1ClsrJ+lRypYpm+aucQonYNnmskQYvDZZKLHMPvQ7OwthWJl715UEX+Tg2fJkJw==
|
||||||
|
|
||||||
"@samverschueren/stream-to-observable@^0.3.0":
|
"@samverschueren/stream-to-observable@^0.3.0":
|
||||||
version "0.3.0"
|
version "0.3.0"
|
||||||
|
@ -162,10 +207,10 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/sizzle" "*"
|
"@types/sizzle" "*"
|
||||||
|
|
||||||
"@types/js-cookie@^2.2.5":
|
"@types/js-cookie@^2.2.6":
|
||||||
version "2.2.5"
|
version "2.2.6"
|
||||||
resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.2.5.tgz#38dfaacae8623b37cc0b0d27398e574e3fc28b1e"
|
resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.2.6.tgz#f1a1cb35aff47bc5cfb05cb0c441ca91e914c26f"
|
||||||
integrity sha512-cpmwBRcHJmmZx0OGU7aPVwGWGbs4iKwVYchk9iuMtxNCA2zorwdaTz4GkLgs2WGxiRZRFKnV1k6tRUHX7tBMxg==
|
integrity sha512-+oY0FDTO2GYKEV0YPvSshGq9t7YozVkgvXLty7zogQNuCxBhT9/3INX9Q7H1aRZ4SUDRXAKlJuA4EA5nTt7SNw==
|
||||||
|
|
||||||
"@types/json-schema@^7.0.3":
|
"@types/json-schema@^7.0.3":
|
||||||
version "7.0.4"
|
version "7.0.4"
|
||||||
|
@ -196,10 +241,10 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/linkify-it" "*"
|
"@types/linkify-it" "*"
|
||||||
|
|
||||||
"@types/node@^13.9.2":
|
"@types/node@^13.11.1":
|
||||||
version "13.9.2"
|
version "13.11.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.9.2.tgz#ace1880c03594cc3e80206d96847157d8e7fa349"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.11.1.tgz#49a2a83df9d26daacead30d0ccc8762b128d53c7"
|
||||||
integrity sha512-bnoqK579sAYrQbp73wwglccjJ4sfRdKU7WNEZ5FW4K2U6Kc0/eZ5kvXG0JKsEKFB50zrFmfFt52/cvBbZa7eXg==
|
integrity sha512-eWQGP3qtxwL8FGneRrC5DwrJLGN4/dH1clNTuLfN81HCrxVtxRjygDTUoZJ5ASlDEeo0ppYFQjQIlXhtXpOn6g==
|
||||||
|
|
||||||
"@types/normalize-package-data@^2.4.0":
|
"@types/normalize-package-data@^2.4.0":
|
||||||
version "2.4.0"
|
version "2.4.0"
|
||||||
|
@ -749,6 +794,14 @@ chalk@^3.0.0:
|
||||||
ansi-styles "^4.1.0"
|
ansi-styles "^4.1.0"
|
||||||
supports-color "^7.1.0"
|
supports-color "^7.1.0"
|
||||||
|
|
||||||
|
chalk@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.0.0.tgz#6e98081ed2d17faab615eb52ac66ec1fe6209e72"
|
||||||
|
integrity sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==
|
||||||
|
dependencies:
|
||||||
|
ansi-styles "^4.1.0"
|
||||||
|
supports-color "^7.1.0"
|
||||||
|
|
||||||
chardet@^0.4.0:
|
chardet@^0.4.0:
|
||||||
version "0.4.2"
|
version "0.4.2"
|
||||||
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2"
|
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2"
|
||||||
|
@ -790,10 +843,10 @@ class-utils@^0.3.5:
|
||||||
isobject "^3.0.0"
|
isobject "^3.0.0"
|
||||||
static-extend "^0.1.1"
|
static-extend "^0.1.1"
|
||||||
|
|
||||||
classcat@^1.1.3:
|
classcat@^4.0.2:
|
||||||
version "1.1.3"
|
version "4.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/classcat/-/classcat-1.1.3.tgz#ec748eecd962ec195a5d8f73f01d67c3d9040912"
|
resolved "https://registry.yarnpkg.com/classcat/-/classcat-4.0.2.tgz#bd5d51b656e01e9cdd21c1aae3d29ed035a52126"
|
||||||
integrity sha512-nuf6HJ5RlEgUUPqN/giIy1wsfA0LJwCHpo/aMGMwEIAxYypbLW/ZdPH4SNrF+OwdrkL3wxJmAs4GPyoE3ZkQ4w==
|
integrity sha512-RlMPOPp8VDu3CJOUVorPumhz/CI+t9ft6f0uexxxCguk28/M+Kf27eQXjNWeDTisEQWei/30oDfITOQqr1TNpQ==
|
||||||
|
|
||||||
clean-css@^4.1.9:
|
clean-css@^4.1.9:
|
||||||
version "4.2.3"
|
version "4.2.3"
|
||||||
|
@ -890,10 +943,10 @@ commander@^4.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.0.tgz#545983a0603fe425bc672d66c9e3c89c42121a83"
|
resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.0.tgz#545983a0603fe425bc672d66c9e3c89c42121a83"
|
||||||
integrity sha512-NIQrwvv9V39FHgGFm36+U9SMQzbiHvU79k+iADraJTpmrFFfx7Ds0IvDoAdZsDrknlkRk14OYoWXb57uTh7/sw==
|
integrity sha512-NIQrwvv9V39FHgGFm36+U9SMQzbiHvU79k+iADraJTpmrFFfx7Ds0IvDoAdZsDrknlkRk14OYoWXb57uTh7/sw==
|
||||||
|
|
||||||
compare-versions@^3.5.1:
|
compare-versions@^3.6.0:
|
||||||
version "3.5.1"
|
version "3.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.5.1.tgz#26e1f5cf0d48a77eced5046b9f67b6b61075a393"
|
resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.6.0.tgz#1a5689913685e5a87637b8d3ffca75514ec41d62"
|
||||||
integrity sha512-9fGPIB7C6AyM18CJJBHt5EnCZDG3oiTJYy0NjfIAGjKpzv0tkxWko7TNQHF5ymqm7IH03tqmeuBxtvD+Izh6mg==
|
integrity sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==
|
||||||
|
|
||||||
component-emitter@^1.2.1:
|
component-emitter@^1.2.1:
|
||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
|
@ -1283,10 +1336,10 @@ eslint-plugin-inferno@^7.14.3:
|
||||||
object.values "^1.1.0"
|
object.values "^1.1.0"
|
||||||
resolve "^1.12.0"
|
resolve "^1.12.0"
|
||||||
|
|
||||||
eslint-plugin-jane@^7.2.0:
|
eslint-plugin-jane@^7.2.1:
|
||||||
version "7.2.0"
|
version "7.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-plugin-jane/-/eslint-plugin-jane-7.2.0.tgz#a2454a6700c644e6c86821ca294adf303e75eddc"
|
resolved "https://registry.yarnpkg.com/eslint-plugin-jane/-/eslint-plugin-jane-7.2.1.tgz#5ffba9ce75e0a5e5dbe3918fc0c5332d2cd89c13"
|
||||||
integrity sha512-/BPZrfxWX9T45gJSf4/2GHfBYgsBYTW7StAQfxL8PxWABZIQKWPWy/5ZokX7UaJlgKHAoC42rJHCQLK5hmfJNA==
|
integrity sha512-hUmhEkHTDq6lQ4oLWZV5cLut9L67fcTiy0USbTsEOx658i9Jdikedt8NJhtamRqO5OUHBGSPU0JkOqBtVNUD+A==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/eslint-plugin" "2.24.0"
|
"@typescript-eslint/eslint-plugin" "2.24.0"
|
||||||
"@typescript-eslint/parser" "2.24.0"
|
"@typescript-eslint/parser" "2.24.0"
|
||||||
|
@ -1300,7 +1353,7 @@ eslint-plugin-jane@^7.2.0:
|
||||||
eslint-plugin-prettier "3.1.2"
|
eslint-plugin-prettier "3.1.2"
|
||||||
eslint-plugin-promise "4.2.1"
|
eslint-plugin-promise "4.2.1"
|
||||||
eslint-plugin-react "7.19.0"
|
eslint-plugin-react "7.19.0"
|
||||||
eslint-plugin-react-hooks "2.5.0"
|
eslint-plugin-react-hooks "2.5.1"
|
||||||
eslint-plugin-unicorn "17.2.0"
|
eslint-plugin-unicorn "17.2.0"
|
||||||
|
|
||||||
eslint-plugin-jest@23.8.2:
|
eslint-plugin-jest@23.8.2:
|
||||||
|
@ -1349,10 +1402,10 @@ eslint-plugin-promise@4.2.1:
|
||||||
resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz#845fd8b2260ad8f82564c1222fce44ad71d9418a"
|
resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz#845fd8b2260ad8f82564c1222fce44ad71d9418a"
|
||||||
integrity sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw==
|
integrity sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw==
|
||||||
|
|
||||||
eslint-plugin-react-hooks@2.5.0:
|
eslint-plugin-react-hooks@2.5.1:
|
||||||
version "2.5.0"
|
version "2.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-2.5.0.tgz#c50ab7ca5945ce6d1cf8248d9e185c80b54171b6"
|
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-2.5.1.tgz#4ef5930592588ce171abeb26f400c7fbcbc23cd0"
|
||||||
integrity sha512-bzvdX47Jx847bgAYf0FPX3u1oxU+mKU8tqrpj4UX9A96SbAmj/HVEefEy6rJUog5u8QIlOPTKZcBpGn5kkKfAQ==
|
integrity sha512-Y2c4b55R+6ZzwtTppKwSmK/Kar8AdLiC2f9NADCuxbcTgPPg41Gyqa6b9GppgXSvCtkRw43ZE86CT5sejKC6/g==
|
||||||
|
|
||||||
eslint-plugin-react@7.19.0:
|
eslint-plugin-react@7.19.0:
|
||||||
version "7.19.0"
|
version "7.19.0"
|
||||||
|
@ -1829,6 +1882,14 @@ fliplog@^0.3.13:
|
||||||
dependencies:
|
dependencies:
|
||||||
chain-able "^1.0.1"
|
chain-able "^1.0.1"
|
||||||
|
|
||||||
|
focus-trap@^5.1.0:
|
||||||
|
version "5.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/focus-trap/-/focus-trap-5.1.0.tgz#64a0bfabd95c382103397dbc96bfef3a3cf8e5ad"
|
||||||
|
integrity sha512-CkB/nrO55069QAUjWFBpX6oc+9V90Qhgpe6fBWApzruMq5gnlh90Oo7iSSDK7pKiV5ugG6OY2AXM5mxcmL3lwQ==
|
||||||
|
dependencies:
|
||||||
|
tabbable "^4.0.0"
|
||||||
|
xtend "^4.0.1"
|
||||||
|
|
||||||
for-in@^1.0.1, for-in@^1.0.2:
|
for-in@^1.0.1, for-in@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
|
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
|
||||||
|
@ -2182,14 +2243,14 @@ human-signals@^1.1.1:
|
||||||
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3"
|
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3"
|
||||||
integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==
|
integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==
|
||||||
|
|
||||||
husky@^4.2.3:
|
husky@^4.2.5:
|
||||||
version "4.2.3"
|
version "4.2.5"
|
||||||
resolved "https://registry.yarnpkg.com/husky/-/husky-4.2.3.tgz#3b18d2ee5febe99e27f2983500202daffbc3151e"
|
resolved "https://registry.yarnpkg.com/husky/-/husky-4.2.5.tgz#2b4f7622673a71579f901d9885ed448394b5fa36"
|
||||||
integrity sha512-VxTsSTRwYveKXN4SaH1/FefRJYCtx+wx04sSVcOpD7N2zjoHxa+cEJ07Qg5NmV3HAK+IRKOyNVpi2YBIVccIfQ==
|
integrity sha512-SYZ95AjKcX7goYVZtVZF2i6XiZcHknw50iXvY7b0MiGoj5RwdgRQNEHdb+gPDPCXKlzwrybjFjkL6FOj8uRhZQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
chalk "^3.0.0"
|
chalk "^4.0.0"
|
||||||
ci-info "^2.0.0"
|
ci-info "^2.0.0"
|
||||||
compare-versions "^3.5.1"
|
compare-versions "^3.6.0"
|
||||||
cosmiconfig "^6.0.0"
|
cosmiconfig "^6.0.0"
|
||||||
find-versions "^3.2.0"
|
find-versions "^3.2.0"
|
||||||
opencollective-postinstall "^2.0.2"
|
opencollective-postinstall "^2.0.2"
|
||||||
|
@ -2198,10 +2259,10 @@ husky@^4.2.3:
|
||||||
slash "^3.0.0"
|
slash "^3.0.0"
|
||||||
which-pm-runs "^1.0.0"
|
which-pm-runs "^1.0.0"
|
||||||
|
|
||||||
i18next@^19.3.3:
|
i18next@^19.4.1:
|
||||||
version "19.3.3"
|
version "19.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/i18next/-/i18next-19.3.3.tgz#04bd79b315e5fe2c87ab8f411e5d55eda0a17bd8"
|
resolved "https://registry.yarnpkg.com/i18next/-/i18next-19.4.1.tgz#4929d15d3d01e4712350a368d005cefa50ff5455"
|
||||||
integrity sha512-CnuPqep5/JsltkGvQqzYN4d79eCe0TreCBRF3a8qHHi8x4SON1qqZ/pvR2X7BfNkNqpA5HXIqw0E731H+VsgSg==
|
integrity sha512-dC3ue15jkLebN2je4xEjfjVYd/fSAo+UVK9f+JxvceCJRowkI+S0lGohgKejqU+FYLfvw9IAPylIIEWwR8Djrg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.3.1"
|
"@babel/runtime" "^7.3.1"
|
||||||
|
|
||||||
|
@ -2814,10 +2875,10 @@ linkify-it@^2.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
uc.micro "^1.0.1"
|
uc.micro "^1.0.1"
|
||||||
|
|
||||||
lint-staged@^10.0.8:
|
lint-staged@^10.1.3:
|
||||||
version "10.0.8"
|
version "10.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.0.8.tgz#0f7849cdc336061f25f5d4fcbcfa385701ff4739"
|
resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.1.3.tgz#da27713d3ac519da305381b4de87d5f866b1d2f1"
|
||||||
integrity sha512-Oa9eS4DJqvQMVdywXfEor6F4vP+21fPHF8LUXgBbVWUSWBddjqsvO6Bv1LwMChmgQZZqwUvgJSHlu8HFHAPZmA==
|
integrity sha512-o2OkLxgVns5RwSC5QF7waeAjJA5nz5gnUfqL311LkZcFipKV7TztrSlhNUK5nQX9H0E5NELAdduMQ+M/JPT7RQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
chalk "^3.0.0"
|
chalk "^3.0.0"
|
||||||
commander "^4.0.1"
|
commander "^4.0.1"
|
||||||
|
@ -3612,10 +3673,10 @@ prettier-linter-helpers@^1.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
fast-diff "^1.1.2"
|
fast-diff "^1.1.2"
|
||||||
|
|
||||||
prettier@^1.18.2:
|
prettier@^2.0.4:
|
||||||
version "1.19.1"
|
version "2.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb"
|
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.4.tgz#2d1bae173e355996ee355ec9830a7a1ee05457ef"
|
||||||
integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==
|
integrity sha512-SVJIQ51spzFDvh4fIbCLvciiDMCrRhlN3mbZvv/+ycjvmF5E73bKdGfU8QDLNmjYJf+lsGnDBC4UUnvTe5OO0w==
|
||||||
|
|
||||||
pretty-time@^0.2.0:
|
pretty-time@^0.2.0:
|
||||||
version "0.2.0"
|
version "0.2.0"
|
||||||
|
@ -4001,13 +4062,20 @@ rx-lite@*, rx-lite@^4.0.8:
|
||||||
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444"
|
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444"
|
||||||
integrity sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=
|
integrity sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=
|
||||||
|
|
||||||
rxjs@^6.3.3, rxjs@^6.4.0, rxjs@^6.5.3:
|
rxjs@^6.3.3, rxjs@^6.5.3:
|
||||||
version "6.5.4"
|
version "6.5.4"
|
||||||
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.4.tgz#e0777fe0d184cec7872df147f303572d414e211c"
|
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.4.tgz#e0777fe0d184cec7872df147f303572d414e211c"
|
||||||
integrity sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==
|
integrity sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
tslib "^1.9.0"
|
tslib "^1.9.0"
|
||||||
|
|
||||||
|
rxjs@^6.5.5:
|
||||||
|
version "6.5.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec"
|
||||||
|
integrity sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==
|
||||||
|
dependencies:
|
||||||
|
tslib "^1.9.0"
|
||||||
|
|
||||||
safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||||
version "5.1.2"
|
version "5.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||||
|
@ -4197,10 +4265,10 @@ snapdragon@^0.8.1:
|
||||||
source-map-resolve "^0.5.0"
|
source-map-resolve "^0.5.0"
|
||||||
use "^3.1.0"
|
use "^3.1.0"
|
||||||
|
|
||||||
sortpack@^2.1.2:
|
sortpack@^2.1.4:
|
||||||
version "2.1.2"
|
version "2.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/sortpack/-/sortpack-2.1.2.tgz#25bf86f2923c81f43a00a2166ff4d271fafeed11"
|
resolved "https://registry.yarnpkg.com/sortpack/-/sortpack-2.1.4.tgz#a2e251c5868455135cc41d3c98a53756a6de5282"
|
||||||
integrity sha512-43fSND1vmAdyfgC38aOkVxZBV331f4blF8acjwQmx7Gba4nuL2ene/Cq5eixNmDhKA/qQHnvSeAl+jEWb31rfg==
|
integrity sha512-RGD0l9kGmuPelXMT8WMMiSv1MkUkaqElB39nMkboIaqVkYns1aaNx263B2EE5QzF1YVUOrBlXnQpd7RX68SSow==
|
||||||
|
|
||||||
source-map-resolve@^0.5.0:
|
source-map-resolve@^0.5.0:
|
||||||
version "0.5.3"
|
version "0.5.3"
|
||||||
|
@ -4473,6 +4541,11 @@ symbol-observable@^1.1.0:
|
||||||
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
|
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
|
||||||
integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==
|
integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==
|
||||||
|
|
||||||
|
tabbable@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-4.0.0.tgz#5bff1d1135df1482cf0f0206434f15eadbeb9261"
|
||||||
|
integrity sha512-H1XoH1URcBOa/rZZWxLxHCtOdVUEev+9vo5YdYhC9tCY4wnybX+VQrCYuy9ubkg69fCBxCONJOSLGfw0DWMffQ==
|
||||||
|
|
||||||
table@^5.2.3:
|
table@^5.2.3:
|
||||||
version "5.4.6"
|
version "5.4.6"
|
||||||
resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e"
|
resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e"
|
||||||
|
@ -4483,10 +4556,10 @@ table@^5.2.3:
|
||||||
slice-ansi "^2.1.0"
|
slice-ansi "^2.1.0"
|
||||||
string-width "^3.0.0"
|
string-width "^3.0.0"
|
||||||
|
|
||||||
terser@^4.6.7:
|
terser@^4.6.11:
|
||||||
version "4.6.7"
|
version "4.6.11"
|
||||||
resolved "https://registry.yarnpkg.com/terser/-/terser-4.6.7.tgz#478d7f9394ec1907f0e488c5f6a6a9a2bad55e72"
|
resolved "https://registry.yarnpkg.com/terser/-/terser-4.6.11.tgz#12ff99fdd62a26de2a82f508515407eb6ccd8a9f"
|
||||||
integrity sha512-fmr7M1f7DBly5cX2+rFDvmGBAaaZyPrHYK4mMdHEDAdNTqXSZgSOfqsfGq2HqPGT/1V0foZZuCZFx8CHKgAk3g==
|
integrity sha512-76Ynm7OXUG5xhOpblhytE7X58oeNSmC8xnNhjWVo8CksHit0U0kO4hfNbPrrYwowLWFgM2n9L176VNx2QaHmtA==
|
||||||
dependencies:
|
dependencies:
|
||||||
commander "^2.20.0"
|
commander "^2.20.0"
|
||||||
source-map "~0.6.1"
|
source-map "~0.6.1"
|
||||||
|
@ -4502,6 +4575,11 @@ through@^2.3.6:
|
||||||
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
|
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
|
||||||
integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
|
integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
|
||||||
|
|
||||||
|
tiny-emitter@^2.1.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
|
||||||
|
integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==
|
||||||
|
|
||||||
tiny-invariant@^1.0.2:
|
tiny-invariant@^1.0.2:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875"
|
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875"
|
||||||
|
@ -4512,12 +4590,12 @@ tiny-warning@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
|
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
|
||||||
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
|
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
|
||||||
|
|
||||||
tippy.js@^6.1.0:
|
tippy.js@^6.1.1:
|
||||||
version "6.1.0"
|
version "6.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-6.1.0.tgz#9c58b94f92f3044d5e861b9d83da3c2a6d3d4323"
|
resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-6.1.1.tgz#9ed09aa4f9c47fb06a0e280e03055f898f5ddfff"
|
||||||
integrity sha512-cRFydlVZlvo4soQSUfVNbH2K77zDUhDAzaAjxseyn81gGIa+j72y98yDL2yB0n8gas/E+Zlr1iOyR5ckslUFqA==
|
integrity sha512-Sk+FPihack9XFbPOc2jRbn6iRLA9my2a8qhaGY6wwD3EeW57/xY5PAPkZOutKVYDWLyNZ/laCkJqg7QJG/gqQw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@popperjs/core" "^2.1.1"
|
"@popperjs/core" "^2.2.0"
|
||||||
|
|
||||||
tmp@^0.0.33:
|
tmp@^0.0.33:
|
||||||
version "0.0.33"
|
version "0.0.33"
|
||||||
|
@ -4581,15 +4659,15 @@ tough-cookie@~2.4.3:
|
||||||
psl "^1.1.24"
|
psl "^1.1.24"
|
||||||
punycode "^1.4.1"
|
punycode "^1.4.1"
|
||||||
|
|
||||||
tributejs@^5.1.2:
|
tributejs@^5.1.3:
|
||||||
version "5.1.2"
|
version "5.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/tributejs/-/tributejs-5.1.2.tgz#d8492d974d3098d6016248d689fb063cda6e77f7"
|
resolved "https://registry.yarnpkg.com/tributejs/-/tributejs-5.1.3.tgz#980600fc72865be5868893078b4bfde721129eae"
|
||||||
integrity sha512-R9ff/q6w4T5f3Y9+RL+qinog3X1eAj1UnR/yfZaGJ8D3wuJs4/vicrGYul9+fgS9EJ/iYgwARekTb92xwark0g==
|
integrity sha512-B5CXihaVzXw+1UHhNFyAwUTMDk1EfoLP5Tj1VhD9yybZ1I8DZJEv8tZ1l0RJo0t0tk9ZhR8eG5tEsaCvRigmdQ==
|
||||||
|
|
||||||
ts-node@^8.7.0:
|
ts-node@^8.8.2:
|
||||||
version "8.7.0"
|
version "8.8.2"
|
||||||
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.7.0.tgz#266186947596bef9f3a034687595b30e31b20976"
|
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.8.2.tgz#0b39e690bee39ea5111513a9d2bcdc0bc121755f"
|
||||||
integrity sha512-s659CsHrsxaRVDEleuOkGvbsA0rWHtszUNEt1r0CgAFN5ZZTQtDzpsluS7W5pOGJIa1xZE8R/zK4dEs+ldFezg==
|
integrity sha512-duVj6BpSpUpD/oM4MfhO98ozgkp3Gt9qIp3jGxwU2DFvl/3IRaEAvbLa8G60uS7C77457e/m5TMowjedeRxI1Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
arg "^4.1.0"
|
arg "^4.1.0"
|
||||||
diff "^4.0.1"
|
diff "^4.0.1"
|
||||||
|
@ -4597,17 +4675,20 @@ ts-node@^8.7.0:
|
||||||
source-map-support "^0.5.6"
|
source-map-support "^0.5.6"
|
||||||
yn "3.1.1"
|
yn "3.1.1"
|
||||||
|
|
||||||
ts-transform-classcat@^0.0.2:
|
ts-transform-classcat@^1.0.0:
|
||||||
version "0.0.2"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/ts-transform-classcat/-/ts-transform-classcat-0.0.2.tgz#2386c9418f3a7c1f03261ff51225b70d0a7664fb"
|
resolved "https://registry.yarnpkg.com/ts-transform-classcat/-/ts-transform-classcat-1.0.0.tgz#6ae1be1b32f1f3c6b1c4232daf8a28e3ced0b62f"
|
||||||
integrity sha512-7laOOhgVxWVqvhK10mIEfedJx2nnNOS8J4P/6a/ehXtHFvsBVRRS9/FcTifgzJweOScZsF5BRD5VOGeNidMSqQ==
|
integrity sha512-LWXEYvBwHDOqBBtoDWSUmbPMsw8FI9vD4XZm98RgziN9UCIj5MRtpmXuP5YYoimCTlPU+D4TFR3IqS+5xSzWsQ==
|
||||||
dependencies:
|
|
||||||
typescript "^2.6.2"
|
|
||||||
|
|
||||||
ts-transform-inferno@^4.0.2:
|
ts-transform-inferno@^4.0.3:
|
||||||
version "4.0.2"
|
version "4.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/ts-transform-inferno/-/ts-transform-inferno-4.0.2.tgz#06b9be45edf874ba7a6ebfb6107ba782509c6afe"
|
resolved "https://registry.yarnpkg.com/ts-transform-inferno/-/ts-transform-inferno-4.0.3.tgz#2cc0eb125abdaff24b8298106a618ab7c6319edc"
|
||||||
integrity sha512-CZb4+w/2l2zikPZ/c51fi3n+qnR2HCEfAS73oGQB80aqRLffkZqm25kYYTMmqUW2+oVfs4M5AZa0z14cvxlQ5w==
|
integrity sha512-Pcg0PVQwJ7Fpv4+3R9obFNsrNKQyLbmUqsjeG7T7r4/4UTgIl0MSwurexjtuGpCp2iv2X/i9ffKPAfAOyYJ9og==
|
||||||
|
|
||||||
|
tslib@^1.10.0:
|
||||||
|
version "1.11.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35"
|
||||||
|
integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==
|
||||||
|
|
||||||
tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0:
|
tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0:
|
||||||
version "1.10.0"
|
version "1.10.0"
|
||||||
|
@ -4638,7 +4719,7 @@ twemoji-parser@12.1.3:
|
||||||
resolved "https://registry.yarnpkg.com/twemoji-parser/-/twemoji-parser-12.1.3.tgz#916c0153e77bd5f1011e7a99cbeacf52e43c9371"
|
resolved "https://registry.yarnpkg.com/twemoji-parser/-/twemoji-parser-12.1.3.tgz#916c0153e77bd5f1011e7a99cbeacf52e43c9371"
|
||||||
integrity sha512-ND4LZXF4X92/PFrzSgGkq6KPPg8swy/U0yRw1k/+izWRVmq1HYi3khPwV3XIB6FRudgVICAaBhJfW8e8G3HC7Q==
|
integrity sha512-ND4LZXF4X92/PFrzSgGkq6KPPg8swy/U0yRw1k/+izWRVmq1HYi3khPwV3XIB6FRudgVICAaBhJfW8e8G3HC7Q==
|
||||||
|
|
||||||
twemoji@^12.1.2:
|
twemoji@^12.1.2, twemoji@^12.1.5:
|
||||||
version "12.1.5"
|
version "12.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/twemoji/-/twemoji-12.1.5.tgz#a961fb65a1afcb1f729ad7e59391f9fe969820b9"
|
resolved "https://registry.yarnpkg.com/twemoji/-/twemoji-12.1.5.tgz#a961fb65a1afcb1f729ad7e59391f9fe969820b9"
|
||||||
integrity sha512-B0PBVy5xomwb1M/WZxf/IqPZfnoIYy1skXnlHjMwLwTNfZ9ljh8VgWQktAPcJXu8080WoEh6YwQGPVhDVqvrVQ==
|
integrity sha512-B0PBVy5xomwb1M/WZxf/IqPZfnoIYy1skXnlHjMwLwTNfZ9ljh8VgWQktAPcJXu8080WoEh6YwQGPVhDVqvrVQ==
|
||||||
|
@ -4673,11 +4754,6 @@ type-is@~1.6.17, type-is@~1.6.18:
|
||||||
media-typer "0.3.0"
|
media-typer "0.3.0"
|
||||||
mime-types "~2.1.24"
|
mime-types "~2.1.24"
|
||||||
|
|
||||||
typescript@^2.6.2:
|
|
||||||
version "2.9.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c"
|
|
||||||
integrity sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==
|
|
||||||
|
|
||||||
typescript@^3.8.3:
|
typescript@^3.8.3:
|
||||||
version "3.8.3"
|
version "3.8.3"
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061"
|
||||||
|
@ -4890,6 +4966,11 @@ xregexp@^4.3.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime-corejs3" "^7.8.3"
|
"@babel/runtime-corejs3" "^7.8.3"
|
||||||
|
|
||||||
|
xtend@^4.0.1:
|
||||||
|
version "4.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
||||||
|
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
|
||||||
|
|
||||||
yaml@^1.7.2:
|
yaml@^1.7.2:
|
||||||
version "1.7.2"
|
version "1.7.2"
|
||||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.7.2.tgz#f26aabf738590ab61efaca502358e48dc9f348b2"
|
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.7.2.tgz#f26aabf738590ab61efaca502358e48dc9f348b2"
|
||||||
|
|
Loading…
Reference in a new issue