Merge branch 'master' into federation

pull/722/head
Dessalines 2020-03-28 15:41:42 -04:00
commit 5ca466117d
40 changed files with 2287 additions and 2063 deletions

1
.gitignore vendored
View File

@ -2,6 +2,7 @@
ansible/inventory ansible/inventory
ansible/inventory_dev ansible/inventory_dev
ansible/passwords/ ansible/passwords/
ansible/vars/
# docker build files # docker build files
docker/lemmy_mine.hjson docker/lemmy_mine.hjson

2
ansible/VERSION vendored
View File

@ -1 +1 @@
v0.6.39 v0.6.44

6
ansible/lemmy.yml vendored
View File

@ -36,13 +36,17 @@
- { src: 'templates/docker-compose.yml', dest: '/lemmy/docker-compose.yml', mode: '0600' } - { src: 'templates/docker-compose.yml', dest: '/lemmy/docker-compose.yml', mode: '0600' }
- { src: 'templates/nginx.conf', dest: '/etc/nginx/sites-enabled/lemmy.conf', mode: '0644' } - { src: 'templates/nginx.conf', dest: '/etc/nginx/sites-enabled/lemmy.conf', mode: '0644' }
- { src: '../docker/iframely.config.local.js', dest: '/lemmy/iframely.config.local.js', mode: '0600' } - { src: '../docker/iframely.config.local.js', dest: '/lemmy/iframely.config.local.js', mode: '0600' }
vars:
lemmy_docker_image: "dessalines/lemmy:{{ lookup('file', 'VERSION') }}"
lemmy_port: "8536"
pictshare_port: "8537"
iframely_port: "8538"
- name: add config file (only during initial setup) - name: add config file (only during initial setup)
template: src='templates/config.hjson' dest='/lemmy/lemmy.hjson' mode='0600' force='no' owner='1000' group='1000' template: src='templates/config.hjson' dest='/lemmy/lemmy.hjson' mode='0600' force='no' owner='1000' group='1000'
vars: vars:
postgres_password: "{{ lookup('password', 'passwords/{{ inventory_hostname }}/postgres chars=ascii_letters,digits') }}" postgres_password: "{{ lookup('password', 'passwords/{{ inventory_hostname }}/postgres chars=ascii_letters,digits') }}"
jwt_password: "{{ lookup('password', 'passwords/{{ inventory_hostname }}/jwt chars=ascii_letters,digits') }}" jwt_password: "{{ lookup('password', 'passwords/{{ inventory_hostname }}/jwt chars=ascii_letters,digits') }}"
lemmy_docker_image: "dessalines/lemmy:{{ lookup('file', 'VERSION') }}"
- name: enable and start docker service - name: enable and start docker service
systemd: systemd:

50
ansible/lemmy_dev.yml vendored
View File

@ -16,6 +16,32 @@
- setup: # gather facts - setup: # gather facts
tasks: tasks:
# TODO: this task is running on all hosts at the same time so there is a race condition
- name: xxx
shell: |
mkdir -p "vars/{{ inventory_hostname }}/"
if [ ! -f "vars/{{ inventory_hostname }}/port_counter" ]; then
if [ -f "vars/max_port_counter" ]; then
MAX_PORT=$(cat vars/max_port_counter)
else
MAX_PORT=8000
fi
OUR_PORT=$(expr $MAX_PORT + 10)
echo $OUR_PORT > "vars/{{ inventory_hostname }}/port_counter"
echo $OUR_PORT > "vars/max_port_counter"
fi
cat "vars/{{ inventory_hostname }}/port_counter"
args:
executable: /bin/bash
delegate_to: localhost
register: lemmy_port
- set_fact: "lemmy_port={{ lemmy_port.stdout_lines[0] }}"
- set_fact: "pictshare_port={{ lemmy_port|int + 1 }}"
- set_fact: "iframely_port={{ lemmy_port|int + 2 }}"
- debug:
msg: "lemmy_port={{ lemmy_port }} pictshare_port={{pictshare_port}} iframely_port={{iframely_port}}"
- name: install dependencies - name: install dependencies
apt: apt:
pkg: ['nginx', 'docker-compose', 'docker.io', 'certbot', 'python-certbot-nginx'] pkg: ['nginx', 'docker-compose', 'docker.io', 'certbot', 'python-certbot-nginx']
@ -25,25 +51,29 @@
args: args:
creates: '/etc/letsencrypt/live/{{domain}}/privkey.pem' creates: '/etc/letsencrypt/live/{{domain}}/privkey.pem'
# TODO: need to use different path per domain
- name: create lemmy folder - name: create lemmy folder
file: path={{item.path}} state=directory file: path={{item.path}} state=directory
with_items: with_items:
- { path: '/lemmy/' } - { path: '/lemmy/{{ domain }}/' }
- { path: '/lemmy/volumes/' } - { path: '/lemmy/{{ domain }}/volumes/' }
- { path: '/var/cache/lemmy/{{ domain }}/' }
- block: - block:
- name: add template files - name: add template files
template: src={{item.src}} dest={{item.dest}} mode={{item.mode}} template: src={{item.src}} dest={{item.dest}} mode={{item.mode}}
with_items: with_items:
- { src: 'templates/docker-compose.yml', dest: '/lemmy/docker-compose.yml', mode: '0600' } - { src: 'templates/docker-compose.yml', dest: '/lemmy/{{domain}}/docker-compose.yml', mode: '0600' }
- { src: 'templates/nginx.conf', dest: '/etc/nginx/sites-enabled/lemmy.conf', mode: '0644' } - { src: 'templates/nginx.conf', dest: '/etc/nginx/sites-enabled/lemmy-{{ domain }}.conf', mode: '0644' }
- { src: '../docker/iframely.config.local.js', dest: '/lemmy/iframely.config.local.js', mode: '0600' } - { src: '../docker/iframely.config.local.js', dest: '/lemmy/{{ domain }}/iframely.config.local.js', mode: '0600' }
- name: add config file (only during initial setup) - name: add config file (only during initial setup)
template: src='templates/config.hjson' dest='/lemmy/lemmy.hjson' mode='0600' force='no' owner='1000' group='1000' template: src='templates/config.hjson' dest='/lemmy/{{domain}}/lemmy.hjson' mode='0600' force='no' owner='1000' group='1000'
vars: vars:
postgres_password: "{{ lookup('password', 'passwords/{{ inventory_hostname }}/postgres chars=ascii_letters,digits') }}" # TODO: these paths are changed, need to move the files
jwt_password: "{{ lookup('password', 'passwords/{{ inventory_hostname }}/jwt chars=ascii_letters,digits') }}" # TODO: not sure what to call the local var folder, its not mentioned in the ansible docs
postgres_password: "{{ lookup('password', 'vars/{{ inventory_hostname }}/postgres_password chars=ascii_letters,digits') }}"
jwt_password: "{{ lookup('password', 'vars/{{ inventory_hostname }}/jwt_password chars=ascii_letters,digits') }}"
- name: build the dev docker image - name: build the dev docker image
local_action: shell cd .. && sudo docker build . -f docker/dev/Dockerfile -t lemmy:dev local_action: shell cd .. && sudo docker build . -f docker/dev/Dockerfile -t lemmy:dev
@ -85,7 +115,7 @@
# be a problem for testing # be a problem for testing
- name: start docker-compose - name: start docker-compose
docker_compose: docker_compose:
project_src: /lemmy/ project_src: "/lemmy/{{ domain }}/"
state: present state: present
recreate: always recreate: always
ignore_errors: yes ignore_errors: yes
@ -96,6 +126,6 @@
- name: certbot renewal cronjob - name: certbot renewal cronjob
cron: cron:
special_time=daily special_time=daily
name=certbot-renew-lemmy name=certbot-renew-lemmy-{{ domain }}
user=root user=root
job="certbot certonly --nginx -d '{{ domain }}' --deploy-hook 'nginx -s reload'" job="certbot certonly --nginx -d '{{ domain }}' --deploy-hook 'nginx -s reload'"

View File

@ -4,10 +4,10 @@ services:
lemmy: lemmy:
image: {{ lemmy_docker_image }} image: {{ lemmy_docker_image }}
ports: ports:
- "127.0.0.1:8536:8536" - "127.0.0.1:{{ lemmy_port }}:8536"
restart: always restart: always
environment: environment:
- RUST_LOG=debug - RUST_LOG=error
volumes: volumes:
- ./lemmy.hjson:/config/config.hjson:ro - ./lemmy.hjson:/config/config.hjson:ro
depends_on: depends_on:
@ -28,7 +28,7 @@ services:
pictshare: pictshare:
image: shtripok/pictshare:latest image: shtripok/pictshare:latest
ports: ports:
- "127.0.0.1:8537:80" - "127.0.0.1:{{ pictshare_port }}:80"
volumes: volumes:
- ./volumes/pictshare:/usr/share/nginx/html/data - ./volumes/pictshare:/usr/share/nginx/html/data
restart: always restart: always
@ -36,7 +36,7 @@ services:
iframely: iframely:
image: dogbin/iframely:latest image: dogbin/iframely:latest
ports: ports:
- "127.0.0.1:8061:80" - "127.0.0.1:{{ iframely_port }}:80"
volumes: volumes:
- ./iframely.config.local.js:/iframely/config.local.js:ro - ./iframely.config.local.js:/iframely/config.local.js:ro
restart: always restart: always

View File

@ -1,4 +1,4 @@
proxy_cache_path /var/cache/lemmy_frontend levels=1:2 keys_zone=lemmy_frontend_cache:10m max_size=100m use_temp_path=off; proxy_cache_path /var/cache/lemmy/{{ domain }} levels=1:2 keys_zone=lemmy_frontend_cache_{{ domain }}:10m max_size=100m use_temp_path=off;
server { server {
listen 80; listen 80;
@ -52,7 +52,7 @@ server {
client_max_body_size 50M; client_max_body_size 50M;
location / { location / {
proxy_pass http://0.0.0.0:8536; proxy_pass http://0.0.0.0:{{ lemmy_port }};
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
@ -63,7 +63,7 @@ server {
proxy_set_header Connection "upgrade"; proxy_set_header Connection "upgrade";
# Proxy Cache # Proxy Cache
proxy_cache lemmy_frontend_cache; proxy_cache lemmy_frontend_cache_{{ domain }};
proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504; proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
proxy_cache_revalidate on; proxy_cache_revalidate on;
proxy_cache_lock on; proxy_cache_lock on;
@ -71,7 +71,7 @@ server {
} }
location /pictshare/ { location /pictshare/ {
proxy_pass http://0.0.0.0:8537/; proxy_pass http://0.0.0.0:{{ pictshare_port }}/;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
@ -82,7 +82,7 @@ server {
} }
location /iframely/ { location /iframely/ {
proxy_pass http://0.0.0.0:8061/; proxy_pass http://0.0.0.0:{{ iframely_port }}/;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
@ -98,6 +98,6 @@ map $remote_addr $remote_addr_anon {
::1 $remote_addr; ::1 $remote_addr;
default 0.0.0.0; default 0.0.0.0;
} }
log_format main '$remote_addr_anon - $remote_user [$time_local] "$request" ' log_format main_{{ domain }} '$remote_addr_anon - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" "$http_user_agent"'; '$status $body_bytes_sent "$http_referer" "$http_user_agent"';
access_log /var/log/nginx/access.log main; access_log /var/log/nginx/access.log main_{{ domain }};

16
ansible/uninstall.yml vendored
View File

@ -22,24 +22,14 @@
- name: stop docker-compose - name: stop docker-compose
docker_compose: docker_compose:
project_src: /lemmy/ project_src: /lemmy/{{domain}}/
state: absent state: absent
- name: delete data - name: delete data
file: path={{item.path}} state=absent file: path={{item.path}} state=absent
with_items: with_items:
- { path: '/lemmy/' } - { path: '/lemmy/{{domain}}/' }
- { path: '/etc/nginx/sites-enabled/lemmy.conf' } - { path: '/etc/nginx/sites-enabled/lemmy-{{ domain }}.conf' }
- name: Remove a volume
docker_volume: name={{item.name}} state=absent
with_items:
- { name: 'lemmy_lemmy_db' }
- { name: 'lemmy_lemmy_pictshare' }
- name: delete entire ecloud folder
file: path='/mnt/repo-base/' state=absent
when: delete_certs|bool
- name: remove certbot cronjob - name: remove certbot cronjob
cron: cron:

View File

@ -10,7 +10,7 @@ RUN yarn install --pure-lockfile
COPY ui /app/ui COPY ui /app/ui
RUN yarn build RUN yarn build
FROM ekidd/rust-musl-builder:1.40.0-openssl11 as rust FROM ekidd/rust-musl-builder:1.42.0-openssl11 as rust
# Cache deps # Cache deps
WORKDIR /app WORKDIR /app
@ -33,7 +33,7 @@ RUN cargo build --frozen --release
# RUN cargo install diesel_cli --no-default-features --features postgres # RUN cargo install diesel_cli --no-default-features --features postgres
FROM ekidd/rust-musl-builder:1.40.0-openssl11 as docs FROM ekidd/rust-musl-builder:1.42.0-openssl11 as docs
WORKDIR /app WORKDIR /app
COPY docs ./docs COPY docs ./docs
RUN sudo chown -R rust:rust . RUN sudo chown -R rust:rust .

View File

@ -20,7 +20,7 @@ COPY ui /app/ui
RUN yarn build RUN yarn build
FROM rust:1.40 as rust FROM rust:1.42 as rust
# Cache deps # Cache deps
WORKDIR /app WORKDIR /app
@ -53,7 +53,7 @@ RUN cp /app/server/target/release/lemmy_server /app/server/ready
#RUN cp /app/server/target/debug/lemmy_server /app/server/ready #RUN cp /app/server/target/debug/lemmy_server /app/server/ready
FROM rust:1.40 as docs FROM rust:1.42 as docs
WORKDIR /app WORKDIR /app

View File

@ -12,12 +12,12 @@ services:
restart: always restart: always
lemmy: lemmy:
image: dessalines/lemmy:v0.6.39 image: dessalines/lemmy:v0.6.44
ports: ports:
- "127.0.0.1:8536:8536" - "127.0.0.1:8536:8536"
restart: always restart: always
environment: environment:
- RUST_LOG=debug - RUST_LOG=error
volumes: volumes:
- ./lemmy.hjson:/config/config.hjson:ro - ./lemmy.hjson:/config/config.hjson:ro
depends_on: depends_on:

View File

@ -47,3 +47,6 @@
- https://docs.rs/activitypub/0.1.4/activitypub/ - https://docs.rs/activitypub/0.1.4/activitypub/
- [Activitypub vocab.](https://www.w3.org/TR/activitystreams-vocabulary/) - [Activitypub vocab.](https://www.w3.org/TR/activitystreams-vocabulary/)
- [Activitypub main](https://www.w3.org/TR/activitypub/) - [Activitypub main](https://www.w3.org/TR/activitypub/)
- [Federation.md](https://github.com/dariusk/gathio/blob/7fc93dbe9d4d99457a0e85c6c532112f415b7af2/FEDERATION.md)
- [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)

View File

@ -2,6 +2,12 @@
Information about contributing to Lemmy, whether it is translating, testing, designing or programming. Information about contributing to Lemmy, whether it is translating, testing, designing or programming.
## Issue tracking / Repositories
- [GitHub (for issues)](https://github.com/dessalines/lemmy)
- [Gitea](https://yerbamate.dev/dessalines/lemmy)
- [GitLab](https://gitlab.com/dessalines/lemmy)
## Translating ## Translating
Go [here](https://github.com/dessalines/lemmy#translations) for translation instructions. Go [here](https://github.com/dessalines/lemmy#translations) for translation instructions.

2435
server/Cargo.lock generated vendored

File diff suppressed because it is too large Load Diff

14
server/Cargo.toml vendored
View File

@ -8,12 +8,12 @@ edition = "2018"
diesel = { version = "1.4.2", features = ["postgres","chrono", "r2d2", "64-column-tables"] } diesel = { version = "1.4.2", features = ["postgres","chrono", "r2d2", "64-column-tables"] }
diesel_migrations = "1.4.0" diesel_migrations = "1.4.0"
dotenv = "0.15.0" dotenv = "0.15.0"
bcrypt = "0.6.1"
activitystreams = "0.5.0-alpha.10" activitystreams = "0.5.0-alpha.10"
bcrypt = "0.6.2"
chrono = { version = "0.4.7", features = ["serde"] } chrono = { version = "0.4.7", features = ["serde"] }
failure = "0.1.5" failure = "0.1.5"
serde_json = { version = "1.0.45", features = ["preserve_order"]} serde_json = { version = "1.0.48", features = ["preserve_order"]}
serde = { version = "1.0.94", features = ["derive"] } serde = { version = "1.0.105", features = ["derive"] }
actix = "0.9.0" actix = "0.9.0"
actix-web = "2.0.0" actix-web = "2.0.0"
actix-files = "0.2.1" actix-files = "0.2.1"
@ -22,10 +22,10 @@ actix-rt = "1.0.0"
log = "0.4.0" log = "0.4.0"
env_logger = "0.7.1" env_logger = "0.7.1"
rand = "0.7.3" rand = "0.7.3"
strum = "0.17.1" strum = "0.18.0"
strum_macros = "0.17.1" strum_macros = "0.18.0"
jsonwebtoken = "7.0.1" jsonwebtoken = "7.0.1"
regex = "1.3.4" regex = "1.3.5"
lazy_static = "1.3.0" lazy_static = "1.3.0"
lettre = "0.9.2" lettre = "0.9.2"
lettre_email = "0.9.2" lettre_email = "0.9.2"
@ -36,4 +36,4 @@ config = "0.10.1"
hjson = "0.8.2" hjson = "0.8.2"
url = "2.1.1" url = "2.1.1"
percent-encoding = "2.1.0" percent-encoding = "2.1.0"
chttp = "0.5.5" isahc = "0.9"

View File

@ -11,8 +11,8 @@ use activitystreams::collection::{OrderedCollection, UnorderedCollection};
use activitystreams::ext::Ext; use activitystreams::ext::Ext;
use activitystreams::object::ObjectBox; use activitystreams::object::ObjectBox;
use activitystreams::object::Page; use activitystreams::object::Page;
use chttp::prelude::*;
use failure::Error; use failure::Error;
use isahc::prelude::*;
use log::warn; use log::warn;
use serde::Deserialize; use serde::Deserialize;
@ -65,7 +65,7 @@ where
} }
// TODO: should cache responses here when we are in production // TODO: should cache responses here when we are in production
// TODO: this function should return a future // TODO: this function should return a future
let text = chttp::get(uri)?.text()?; let text = isahc::get(uri)?.text()?;
let res: Response = serde_json::from_str(&text)?; let res: Response = serde_json::from_str(&text)?;
Ok(res) Ok(res)
} }

View File

@ -34,7 +34,7 @@ pub mod websocket;
use crate::settings::Settings; use crate::settings::Settings;
use chrono::{DateTime, FixedOffset, Local, NaiveDateTime}; use chrono::{DateTime, FixedOffset, Local, NaiveDateTime};
use chttp::prelude::*; use isahc::prelude::*;
use lettre::smtp::authentication::{Credentials, Mechanism}; use lettre::smtp::authentication::{Credentials, Mechanism};
use lettre::smtp::extension::ClientId; use lettre::smtp::extension::ClientId;
use lettre::smtp::ConnectionReuseParameters; use lettre::smtp::ConnectionReuseParameters;
@ -159,7 +159,7 @@ pub struct IframelyResponse {
pub fn fetch_iframely(url: &str) -> Result<IframelyResponse, failure::Error> { pub fn fetch_iframely(url: &str) -> Result<IframelyResponse, failure::Error> {
let fetch_url = format!("http://iframely/oembed?url={}", url); let fetch_url = format!("http://iframely/oembed?url={}", url);
let text = chttp::get(&fetch_url)?.text()?; let text = isahc::get(&fetch_url)?.text()?;
let res: IframelyResponse = serde_json::from_str(&text)?; let res: IframelyResponse = serde_json::from_str(&text)?;
Ok(res) Ok(res)
} }
@ -175,7 +175,7 @@ pub fn fetch_pictshare(image_url: &str) -> Result<PictshareResponse, failure::Er
"http://pictshare/api/geturl.php?url={}", "http://pictshare/api/geturl.php?url={}",
utf8_percent_encode(image_url, NON_ALPHANUMERIC) utf8_percent_encode(image_url, NON_ALPHANUMERIC)
); );
let text = chttp::get(&fetch_url)?.text()?; let text = isahc::get(&fetch_url)?.text()?;
let res: PictshareResponse = serde_json::from_str(&text)?; let res: PictshareResponse = serde_json::from_str(&text)?;
Ok(res) Ok(res)
} }

View File

@ -1 +1 @@
pub const VERSION: &str = "v0.6.39"; pub const VERSION: &str = "v0.6.44";

1
ui/.eslintrc.json vendored
View File

@ -38,6 +38,7 @@
"inferno/no-direct-mutation-state": 0, "inferno/no-direct-mutation-state": 0,
"inferno/no-unknown-property": 0, "inferno/no-unknown-property": 0,
"max-statements": 0, "max-statements": 0,
"max-params": 0,
"new-cap": 0, "new-cap": 0,
"no-console": 0, "no-console": 0,
"no-duplicate-imports": 0, "no-duplicate-imports": 0,

View File

@ -152,7 +152,7 @@ svg.thumbnail {
} }
hr { hr {
border-top: 1px solid var(--secondary); border-top: 1px solid var(--light);
} }
.emoji { .emoji {
@ -186,7 +186,7 @@ hr {
max-height: 90vh; max-height: 90vh;
} }
.vote-animate:active { .btn-animate:active {
transform: scale(1.2); transform: scale(1.2);
-webkit-transform: scale(1.2); -webkit-transform: scale(1.2);
-ms-transform: scale(1.2); -ms-transform: scale(1.2);

26
ui/package.json vendored
View File

@ -15,18 +15,18 @@
"keywords": [], "keywords": [],
"dependencies": { "dependencies": {
"@types/autosize": "^3.0.6", "@types/autosize": "^3.0.6",
"@types/js-cookie": "^2.2.1", "@types/js-cookie": "^2.2.5",
"@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.7.0", "@types/node": "^13.9.2",
"autosize": "^4.0.2", "autosize": "^4.0.2",
"bootswatch": "^4.3.1", "bootswatch": "^4.3.1",
"classcat": "^1.1.3", "classcat": "^1.1.3",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"emoji-short-name": "^1.0.0", "emoji-short-name": "^1.0.0",
"husky": "^4.2.1", "husky": "^4.2.3",
"i18next": "^19.0.3", "i18next": "^19.3.3",
"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",
@ -40,21 +40,21 @@
"prettier": "^1.18.2", "prettier": "^1.18.2",
"reconnecting-websocket": "^4.4.0", "reconnecting-websocket": "^4.4.0",
"rxjs": "^6.4.0", "rxjs": "^6.4.0",
"terser": "^4.6.3", "terser": "^4.6.7",
"tippy.js": "^6.0.0", "tippy.js": "^6.1.0",
"toastify-js": "^1.6.2", "toastify-js": "^1.7.0",
"tributejs": "^5.0.0", "tributejs": "^5.1.2",
"twemoji": "^12.1.2", "twemoji": "^12.1.2",
"ws": "^7.0.0" "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.0.2", "eslint-plugin-jane": "^7.2.0",
"fuse-box": "^3.1.3", "fuse-box": "^3.1.3",
"lint-staged": "^10.0.2", "lint-staged": "^10.0.8",
"sortpack": "^2.0.1", "sortpack": "^2.1.2",
"ts-node": "^8.6.2", "ts-node": "^8.7.0",
"ts-transform-classcat": "^0.0.2", "ts-transform-classcat": "^0.0.2",
"ts-transform-inferno": "^4.0.2", "ts-transform-inferno": "^4.0.2",
"typescript": "^3.8.3" "typescript": "^3.8.3"

View File

@ -56,6 +56,8 @@ interface CommentNodeState {
upvotes: number; upvotes: number;
downvotes: number; downvotes: number;
borderColor: string; borderColor: string;
readLoading: boolean;
saveLoading: boolean;
} }
interface CommentNodeProps { interface CommentNodeProps {
@ -64,6 +66,7 @@ interface CommentNodeProps {
viewOnly?: boolean; viewOnly?: boolean;
locked?: boolean; locked?: boolean;
markable?: boolean; markable?: boolean;
showContext?: boolean;
moderators: Array<CommunityUser>; moderators: Array<CommunityUser>;
admins: Array<UserView>; admins: Array<UserView>;
// TODO is this necessary, can't I get it from the node itself? // TODO is this necessary, can't I get it from the node itself?
@ -97,6 +100,8 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
borderColor: this.props.node.comment.depth borderColor: this.props.node.comment.depth
? colorList[this.props.node.comment.depth % colorList.length] ? colorList[this.props.node.comment.depth % colorList.length]
: colorList[0], : colorList[0],
readLoading: false,
saveLoading: false,
}; };
constructor(props: any, context: any) { constructor(props: any, context: any) {
@ -113,6 +118,8 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
this.state.upvotes = nextProps.node.comment.upvotes; this.state.upvotes = nextProps.node.comment.upvotes;
this.state.downvotes = nextProps.node.comment.downvotes; this.state.downvotes = nextProps.node.comment.downvotes;
this.state.score = nextProps.node.comment.score; this.state.score = nextProps.node.comment.score;
this.state.readLoading = false;
this.state.saveLoading = false;
this.setState(this.state); this.setState(this.state);
} }
@ -121,18 +128,12 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
return ( return (
<div <div
className={`comment ${ className={`comment ${
node.comment.parent_id && !this.props.noIndent ? 'ml-2' : '' node.comment.parent_id && !this.props.noIndent ? 'ml-1' : ''
}`} }`}
> >
{!node.comment.parent_id && !this.props.noIndent && (
<>
<hr class="d-sm-none my-2" />
<div class="d-none d-sm-block d-sm-none my-3" />
</>
)}
<div <div
id={`comment-${node.comment.id}`} id={`comment-${node.comment.id}`}
className={`details comment-node mb-1 ${ className={`details comment-node border-top border-light ${
this.isCommentNew ? 'mark' : '' this.isCommentNew ? 'mark' : ''
}`} }`}
style={ style={
@ -146,75 +147,51 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
this.props.node.comment.parent_id && this.props.node.comment.parent_id &&
'ml-2'}`} 'ml-2'}`}
> >
<ul class="list-inline mb-1 text-muted small"> <div class="d-flex flex-wrap align-items-center mb-1 mt-1 text-muted small">
<li className="list-inline-item">
<Link <Link
className="text-body font-weight-bold" className="mr-2 text-body font-weight-bold"
to={`/u/${node.comment.creator_name}`} to={`/u/${node.comment.creator_name}`}
> >
{node.comment.creator_avatar && showAvatars() && ( {node.comment.creator_avatar && showAvatars() && (
<img <img
height="32" height="32"
width="32" width="32"
src={pictshareAvatarThumbnail( src={pictshareAvatarThumbnail(node.comment.creator_avatar)}
node.comment.creator_avatar
)}
class="rounded-circle mr-1" class="rounded-circle mr-1"
/> />
)} )}
<span>{node.comment.creator_name}</span> <span>{node.comment.creator_name}</span>
</Link> </Link>
</li>
{this.isMod && ( {this.isMod && (
<li className="list-inline-item badge badge-light"> <div className="badge badge-light d-none d-sm-inline mr-2">
{i18n.t('mod')} {i18n.t('mod')}
</li> </div>
)} )}
{this.isAdmin && ( {this.isAdmin && (
<li className="list-inline-item badge badge-light"> <div className="badge badge-light d-none d-sm-inline mr-2">
{i18n.t('admin')} {i18n.t('admin')}
</li> </div>
)} )}
{this.isPostCreator && ( {this.isPostCreator && (
<li className="list-inline-item badge badge-light"> <div className="badge badge-light d-none d-sm-inline mr-2">
{i18n.t('creator')} {i18n.t('creator')}
</li> </div>
)} )}
{(node.comment.banned_from_community || node.comment.banned) && ( {(node.comment.banned_from_community || node.comment.banned) && (
<li className="list-inline-item badge badge-danger"> <div className="badge badge-danger mr-2">
{i18n.t('banned')} {i18n.t('banned')}
</li> </div>
)} )}
{this.props.showCommunity && ( {this.props.showCommunity && (
<li className="list-inline-item"> <>
<span> {i18n.t('to')} </span> <span class="mx-1">{i18n.t('to')}</span>
<Link to={`/c/${node.comment.community_name}`}> <Link class="mr-2" to={`/c/${node.comment.community_name}`}>
{node.comment.community_name} {node.comment.community_name}
</Link> </Link>
</li> </>
)} )}
<li className="list-inline-item"></li>
<li className="list-inline-item">
<span
className={`unselectable pointer ${this.scoreColor}`}
onClick={linkEvent(node, this.handleCommentUpvote)}
data-tippy-content={this.pointsTippy}
>
<svg class="icon icon-inline mr-1">
<use xlinkHref="#icon-zap"></use>
</svg>
{this.state.score}
</span>
</li>
<li className="list-inline-item"></li>
<li className="list-inline-item">
<span>
<MomentTime data={node.comment} />
</span>
</li>
<li className="list-inline-item">
<div <div
className="unselectable pointer text-monospace" className="mr-lg-4 flex-grow-1 flex-lg-grow-0 unselectable pointer mr-2"
onClick={linkEvent(this, this.handleCommentCollapse)} onClick={linkEvent(this, this.handleCommentCollapse)}
> >
{this.state.collapsed ? ( {this.state.collapsed ? (
@ -227,8 +204,22 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
</svg> </svg>
)} )}
</div> </div>
</li> <span
</ul> className={`unselectable pointer ${this.scoreColor}`}
onClick={linkEvent(node, this.handleCommentUpvote)}
data-tippy-content={this.pointsTippy}
>
<svg class="icon icon-inline mr-1">
<use xlinkHref="#icon-zap"></use>
</svg>
<span class="mr-1">{this.state.score}</span>
</span>
<span className="mr-1"></span>
<span>
<MomentTime data={node.comment} />
</span>
</div>
{/* end of user row */}
{this.state.showEdit && ( {this.state.showEdit && (
<CommentForm <CommentForm
node={node} node={node}
@ -249,11 +240,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
)} )}
/> />
)} )}
<ul class="list-inline mb-0 text-muted font-weight-bold h5"> <div class="d-flex justify-content-between justify-content-lg-start flex-wrap text-muted font-weight-bold">
{this.props.showContext && this.linkBtn}
{this.props.markable && ( {this.props.markable && (
<li className="list-inline-item-action"> <button
<span class="btn btn-link btn-animate text-muted"
class="pointer"
onClick={linkEvent(this, this.handleMarkRead)} onClick={linkEvent(this, this.handleMarkRead)}
data-tippy-content={ data-tippy-content={
node.comment.read node.comment.read
@ -261,20 +252,22 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
: i18n.t('mark_as_read') : i18n.t('mark_as_read')
} }
> >
{this.state.readLoading ? (
this.loadingIcon
) : (
<svg <svg
class={`icon icon-inline ${node.comment.read && class={`icon icon-inline ${node.comment.read &&
'text-success'}`} 'text-success'}`}
> >
<use xlinkHref="#icon-check"></use> <use xlinkHref="#icon-check"></use>
</svg> </svg>
</span> )}
</li> </button>
)} )}
{UserService.Instance.user && !this.props.viewOnly && ( {UserService.Instance.user && !this.props.viewOnly && (
<> <>
<li className="list-inline-item-action">
<button <button
className={`vote-animate btn btn-link p-0 mb-1 ${ className={`btn btn-link btn-animate ${
this.state.my_vote == 1 ? 'text-info' : 'text-muted' this.state.my_vote == 1 ? 'text-info' : 'text-muted'
}`} }`}
onClick={linkEvent(node, this.handleCommentUpvote)} onClick={linkEvent(node, this.handleCommentUpvote)}
@ -287,19 +280,14 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
<span class="ml-1">{this.state.upvotes}</span> <span class="ml-1">{this.state.upvotes}</span>
)} )}
</button> </button>
</li>
{WebSocketService.Instance.site.enable_downvotes && ( {WebSocketService.Instance.site.enable_downvotes && (
<li className="list-inline-item-action">
<button <button
className={`vote-animate btn btn-link p-0 mb-1 ${ className={`btn btn-link btn-animate ${
this.state.my_vote == -1 this.state.my_vote == -1
? 'text-danger' ? 'text-danger'
: 'text-muted' : 'text-muted'
}`} }`}
onClick={linkEvent( onClick={linkEvent(node, this.handleCommentDownvote)}
node,
this.handleCommentDownvote
)}
data-tippy-content={i18n.t('downvote')} data-tippy-content={i18n.t('downvote')}
> >
<svg class="icon icon-inline"> <svg class="icon icon-inline">
@ -309,46 +297,48 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
<span class="ml-1">{this.state.downvotes}</span> <span class="ml-1">{this.state.downvotes}</span>
)} )}
</button> </button>
</li>
)} )}
<li className="list-inline-item-action"> <button
<span class="btn btn-link btn-animate text-muted"
class="pointer" onClick={linkEvent(this, this.handleSaveCommentClick)}
data-tippy-content={
node.comment.saved ? i18n.t('unsave') : i18n.t('save')
}
>
{this.state.saveLoading ? (
this.loadingIcon
) : (
<svg
class={`icon icon-inline ${node.comment.saved &&
'text-warning'}`}
>
<use xlinkHref="#icon-star"></use>
</svg>
)}
</button>
<button
class="btn btn-link btn-animate text-muted"
onClick={linkEvent(this, this.handleReplyClick)} onClick={linkEvent(this, this.handleReplyClick)}
data-tippy-content={i18n.t('reply')} data-tippy-content={i18n.t('reply')}
> >
<svg class="icon icon-inline"> <svg class="icon icon-inline">
<use xlinkHref="#icon-reply1"></use> <use xlinkHref="#icon-reply1"></use>
</svg> </svg>
</span> </button>
</li>
<li className="list-inline-item-action">
<Link
className="text-muted"
to={`/post/${node.comment.post_id}/comment/${node.comment.id}`}
title={i18n.t('link')}
>
<svg class="icon icon-inline">
<use xlinkHref="#icon-link"></use>
</svg>
</Link>
</li>
{!this.state.showAdvanced ? ( {!this.state.showAdvanced ? (
<li className="list-inline-item-action"> <button
<span className="btn btn-link btn-animate text-muted"
className="unselectable pointer"
onClick={linkEvent(this, this.handleShowAdvanced)} onClick={linkEvent(this, this.handleShowAdvanced)}
data-tippy-content={i18n.t('more')} data-tippy-content={i18n.t('more')}
> >
<svg class="icon icon-inline"> <svg class="icon icon-inline">
<use xlinkHref="#icon-more-vertical"></use> <use xlinkHref="#icon-more-vertical"></use>
</svg> </svg>
</span> </button>
</li>
) : ( ) : (
<> <>
{!this.myComment && ( {!this.myComment && (
<li className="list-inline-item-action"> <button class="btn btn-link btn-animate">
<Link <Link
class="text-muted" class="text-muted"
to={`/create_private_message?recipient_id=${node.comment.creator_id}`} to={`/create_private_message?recipient_id=${node.comment.creator_id}`}
@ -358,32 +348,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
<use xlinkHref="#icon-mail"></use> <use xlinkHref="#icon-mail"></use>
</svg> </svg>
</Link> </Link>
</li> </button>
)} )}
<li className="list-inline-item-action"> {!this.props.showContext && this.linkBtn}
<span <button
class="pointer" className="btn btn-link btn-animate text-muted"
onClick={linkEvent(
this,
this.handleSaveCommentClick
)}
data-tippy-content={
node.comment.saved
? i18n.t('unsave')
: i18n.t('save')
}
>
<svg
class={`icon icon-inline ${node.comment.saved &&
'text-warning'}`}
>
<use xlinkHref="#icon-star"></use>
</svg>
</span>
</li>
<li className="list-inline-item-action">
<span
className="pointer"
onClick={linkEvent(this, this.handleViewSource)} onClick={linkEvent(this, this.handleViewSource)}
data-tippy-content={i18n.t('view_source')} data-tippy-content={i18n.t('view_source')}
> >
@ -393,28 +362,20 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
> >
<use xlinkHref="#icon-file-text"></use> <use xlinkHref="#icon-file-text"></use>
</svg> </svg>
</span> </button>
</li>
{this.myComment && ( {this.myComment && (
<> <>
<li className="list-inline-item-action"></li> <button
<li className="list-inline-item-action"> class="btn btn-link btn-animate text-muted"
<span onClick={linkEvent(this, this.handleEditClick)}
class="pointer"
onClick={linkEvent(
this,
this.handleEditClick
)}
data-tippy-content={i18n.t('edit')} data-tippy-content={i18n.t('edit')}
> >
<svg class="icon icon-inline"> <svg class="icon icon-inline">
<use xlinkHref="#icon-edit"></use> <use xlinkHref="#icon-edit"></use>
</svg> </svg>
</span> </button>
</li> <button
<li className="list-inline-item-action"> class="btn btn-link btn-animate text-muted"
<span
class="pointer"
onClick={linkEvent( onClick={linkEvent(
this, this,
this.handleDeleteClick this.handleDeleteClick
@ -431,71 +392,64 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
> >
<use xlinkHref="#icon-trash"></use> <use xlinkHref="#icon-trash"></use>
</svg> </svg>
</span> </button>
</li>
</> </>
)} )}
{/* Admins and mods can remove comments */} {/* Admins and mods can remove comments */}
{(this.canMod || this.canAdmin) && ( {(this.canMod || this.canAdmin) && (
<> <>
<li className="list-inline-item-action">
{!node.comment.removed ? ( {!node.comment.removed ? (
<span <button
class="pointer" class="btn btn-link btn-animate text-muted"
onClick={linkEvent( onClick={linkEvent(
this, this,
this.handleModRemoveShow this.handleModRemoveShow
)} )}
> >
{i18n.t('remove')} {i18n.t('remove')}
</span> </button>
) : ( ) : (
<span <button
class="pointer" class="btn btn-link btn-animate text-muted"
onClick={linkEvent( onClick={linkEvent(
this, this,
this.handleModRemoveSubmit this.handleModRemoveSubmit
)} )}
> >
{i18n.t('restore')} {i18n.t('restore')}
</span> </button>
)} )}
</li>
</> </>
)} )}
{/* Mods can ban from community, and appoint as mods to community */} {/* Mods can ban from community, and appoint as mods to community */}
{this.canMod && ( {this.canMod && (
<> <>
{!this.isMod && ( {!this.isMod &&
<li className="list-inline-item-action"> (!node.comment.banned_from_community ? (
{!node.comment.banned_from_community ? ( <button
<span class="btn btn-link btn-animate text-muted"
class="pointer"
onClick={linkEvent( onClick={linkEvent(
this, this,
this.handleModBanFromCommunityShow this.handleModBanFromCommunityShow
)} )}
> >
{i18n.t('ban')} {i18n.t('ban')}
</span> </button>
) : ( ) : (
<span <button
class="pointer" class="btn btn-link btn-animate text-muted"
onClick={linkEvent( onClick={linkEvent(
this, this,
this.handleModBanFromCommunitySubmit this.handleModBanFromCommunitySubmit
)} )}
> >
{i18n.t('unban')} {i18n.t('unban')}
</span> </button>
)} ))}
</li> {!node.comment.banned_from_community &&
)} (!this.state.showConfirmAppointAsMod ? (
{!node.comment.banned_from_community && ( <button
<li className="list-inline-item-action"> class="btn btn-link btn-animate text-muted"
{!this.state.showConfirmAppointAsMod ? (
<span
class="pointer"
onClick={linkEvent( onClick={linkEvent(
this, this,
this.handleShowConfirmAppointAsMod this.handleShowConfirmAppointAsMod
@ -504,66 +458,63 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
{this.isMod {this.isMod
? i18n.t('remove_as_mod') ? i18n.t('remove_as_mod')
: i18n.t('appoint_as_mod')} : i18n.t('appoint_as_mod')}
</span> </button>
) : ( ) : (
<> <>
<span class="d-inline-block mr-1"> <button class="btn btn-link btn-animate text-muted">
{i18n.t('are_you_sure')} {i18n.t('are_you_sure')}
</span> </button>
<span <button
class="pointer d-inline-block mr-1" class="btn btn-link btn-animate text-muted"
onClick={linkEvent( onClick={linkEvent(
this, this,
this.handleAddModToCommunity this.handleAddModToCommunity
)} )}
> >
{i18n.t('yes')} {i18n.t('yes')}
</span> </button>
<span <button
class="pointer d-inline-block" class="btn btn-link btn-animate text-muted"
onClick={linkEvent( onClick={linkEvent(
this, this,
this.handleCancelConfirmAppointAsMod this.handleCancelConfirmAppointAsMod
)} )}
> >
{i18n.t('no')} {i18n.t('no')}
</span> </button>
</> </>
)} ))}
</li>
)}
</> </>
)} )}
{/* Community creators and admins can transfer community to another mod */} {/* Community creators and admins can transfer community to another mod */}
{(this.amCommunityCreator || this.canAdmin) && {(this.amCommunityCreator || this.canAdmin) &&
this.isMod && ( this.isMod &&
<li className="list-inline-item-action"> (!this.state.showConfirmTransferCommunity ? (
{!this.state.showConfirmTransferCommunity ? ( <button
<span class="btn btn-link btn-animate text-muted"
class="pointer"
onClick={linkEvent( onClick={linkEvent(
this, this,
this.handleShowConfirmTransferCommunity this.handleShowConfirmTransferCommunity
)} )}
> >
{i18n.t('transfer_community')} {i18n.t('transfer_community')}
</span> </button>
) : ( ) : (
<> <>
<span class="d-inline-block mr-1"> <button class="btn btn-link btn-animate text-muted">
{i18n.t('are_you_sure')} {i18n.t('are_you_sure')}
</span> </button>
<span <button
class="pointer d-inline-block mr-1" class="btn btn-link btn-animate text-muted"
onClick={linkEvent( onClick={linkEvent(
this, this,
this.handleTransferCommunity this.handleTransferCommunity
)} )}
> >
{i18n.t('yes')} {i18n.t('yes')}
</span> </button>
<span <button
class="pointer d-inline-block" class="btn btn-link btn-animate text-muted"
onClick={linkEvent( onClick={linkEvent(
this, this,
this this
@ -571,44 +522,38 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
)} )}
> >
{i18n.t('no')} {i18n.t('no')}
</span> </button>
</> </>
)} ))}
</li>
)}
{/* Admins can ban from all, and appoint other admins */} {/* Admins can ban from all, and appoint other admins */}
{this.canAdmin && ( {this.canAdmin && (
<> <>
{!this.isAdmin && ( {!this.isAdmin &&
<li className="list-inline-item-action"> (!node.comment.banned ? (
{!node.comment.banned ? ( <button
<span class="btn btn-link btn-animate text-muted"
class="pointer"
onClick={linkEvent( onClick={linkEvent(
this, this,
this.handleModBanShow this.handleModBanShow
)} )}
> >
{i18n.t('ban_from_site')} {i18n.t('ban_from_site')}
</span> </button>
) : ( ) : (
<span <button
class="pointer" class="btn btn-link btn-animate text-muted"
onClick={linkEvent( onClick={linkEvent(
this, this,
this.handleModBanSubmit this.handleModBanSubmit
)} )}
> >
{i18n.t('unban_from_site')} {i18n.t('unban_from_site')}
</span> </button>
)} ))}
</li> {!node.comment.banned &&
)} (!this.state.showConfirmAppointAsAdmin ? (
{!node.comment.banned && ( <button
<li className="list-inline-item-action"> class="btn btn-link btn-animate text-muted"
{!this.state.showConfirmAppointAsAdmin ? (
<span
class="pointer"
onClick={linkEvent( onClick={linkEvent(
this, this,
this.handleShowConfirmAppointAsAdmin this.handleShowConfirmAppointAsAdmin
@ -617,81 +562,78 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
{this.isAdmin {this.isAdmin
? i18n.t('remove_as_admin') ? i18n.t('remove_as_admin')
: i18n.t('appoint_as_admin')} : i18n.t('appoint_as_admin')}
</span> </button>
) : ( ) : (
<> <>
<span class="d-inline-block mr-1"> <button class="btn btn-link btn-animate text-muted">
{i18n.t('are_you_sure')} {i18n.t('are_you_sure')}
</span> </button>
<span <button
class="pointer d-inline-block mr-1" class="btn btn-link btn-animate text-muted"
onClick={linkEvent( onClick={linkEvent(
this, this,
this.handleAddAdmin this.handleAddAdmin
)} )}
> >
{i18n.t('yes')} {i18n.t('yes')}
</span> </button>
<span <button
class="pointer d-inline-block" class="btn btn-link btn-animate text-muted"
onClick={linkEvent( onClick={linkEvent(
this, this,
this.handleCancelConfirmAppointAsAdmin this.handleCancelConfirmAppointAsAdmin
)} )}
> >
{i18n.t('no')} {i18n.t('no')}
</span> </button>
</> </>
)} ))}
</li>
)}
</> </>
)} )}
{/* Site Creator can transfer to another admin */} {/* Site Creator can transfer to another admin */}
{this.amSiteCreator && this.isAdmin && ( {this.amSiteCreator &&
<li className="list-inline-item-action"> this.isAdmin &&
{!this.state.showConfirmTransferSite ? ( (!this.state.showConfirmTransferSite ? (
<span <button
class="pointer" class="btn btn-link btn-animate text-muted"
onClick={linkEvent( onClick={linkEvent(
this, this,
this.handleShowConfirmTransferSite this.handleShowConfirmTransferSite
)} )}
> >
{i18n.t('transfer_site')} {i18n.t('transfer_site')}
</span> </button>
) : ( ) : (
<> <>
<span class="d-inline-block mr-1"> <button class="btn btn-link btn-animate text-muted">
{i18n.t('are_you_sure')} {i18n.t('are_you_sure')}
</span> </button>
<span <button
class="pointer d-inline-block mr-1" class="btn btn-link btn-animate text-muted"
onClick={linkEvent( onClick={linkEvent(
this, this,
this.handleTransferSite this.handleTransferSite
)} )}
> >
{i18n.t('yes')} {i18n.t('yes')}
</span> </button>
<span <button
class="pointer d-inline-block" class="btn btn-link btn-animate text-muted"
onClick={linkEvent( onClick={linkEvent(
this, this,
this.handleCancelShowConfirmTransferSite this.handleCancelShowConfirmTransferSite
)} )}
> >
{i18n.t('no')} {i18n.t('no')}
</span> </button>
</> </>
)} ))}
</li>
)}
</> </>
)} )}
</> </>
)} )}
</ul> </div>
{/* end of button group */}
</div> </div>
)} )}
</div> </div>
@ -762,6 +704,33 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
); );
} }
get linkBtn() {
let node = this.props.node;
return (
<button className="btn btn-link btn-animate">
<Link
class="text-muted"
to={`/post/${node.comment.post_id}/comment/${node.comment.id}`}
title={
this.props.showContext ? i18n.t('show_context') : i18n.t('link')
}
>
<svg class="icon icon-inline">
<use xlinkHref="#icon-link"></use>
</svg>
</Link>
</button>
);
}
get loadingIcon() {
return (
<svg class="icon icon-spinner spin">
<use xlinkHref="#icon-spinner"></use>
</svg>
);
}
get myComment(): boolean { get myComment(): boolean {
return ( return (
UserService.Instance.user && UserService.Instance.user &&
@ -881,6 +850,9 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
}; };
WebSocketService.Instance.saveComment(form); WebSocketService.Instance.saveComment(form);
i.state.saveLoading = true;
i.setState(this.state);
} }
handleReplyCancel() { handleReplyCancel() {
@ -993,6 +965,9 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
}; };
WebSocketService.Instance.editComment(form); WebSocketService.Instance.editComment(form);
} }
i.state.readLoading = true;
i.setState(this.state);
} }
handleModBanFromCommunityShow(i: CommentNode) { handleModBanFromCommunityShow(i: CommentNode) {

View File

@ -20,6 +20,7 @@ interface CommentNodesProps {
viewOnly?: boolean; viewOnly?: boolean;
locked?: boolean; locked?: boolean;
markable?: boolean; markable?: boolean;
showContext?: boolean;
showCommunity?: boolean; showCommunity?: boolean;
sort?: CommentSortType; sort?: CommentSortType;
sortType?: SortType; sortType?: SortType;
@ -47,6 +48,7 @@ export class CommentNodes extends Component<
admins={this.props.admins} admins={this.props.admins}
postCreatorId={this.props.postCreatorId} postCreatorId={this.props.postCreatorId}
markable={this.props.markable} markable={this.props.markable}
showContext={this.props.showContext}
showCommunity={this.props.showCommunity} showCommunity={this.props.showCommunity}
sort={this.props.sort} sort={this.props.sort}
sortType={this.props.sortType} sortType={this.props.sortType}

View File

@ -189,6 +189,7 @@ export class Community extends Component<any, State> {
nodes={commentsToFlatNodes(this.state.comments)} nodes={commentsToFlatNodes(this.state.comments)}
noIndent noIndent
sortType={this.state.sort} sortType={this.state.sort}
showContext
/> />
); );
} }

View File

@ -1,5 +1,4 @@
import { Component, linkEvent } from 'inferno'; import { Component, linkEvent } from 'inferno';
import { Link } from 'inferno-router';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators'; import { retryWhen, delay, take } from 'rxjs/operators';
import { import {
@ -34,14 +33,13 @@ import { CommentNodes } from './comment-nodes';
import { PrivateMessage } from './private-message'; import { PrivateMessage } from './private-message';
import { SortSelect } from './sort-select'; import { SortSelect } from './sort-select';
import { i18n } from '../i18next'; import { i18n } from '../i18next';
import { T } from 'inferno-i18next';
enum UnreadOrAll { enum UnreadOrAll {
Unread, Unread,
All, All,
} }
enum UnreadType { enum MessageType {
All, All,
Replies, Replies,
Mentions, Mentions,
@ -52,7 +50,7 @@ type ReplyType = Comment | PrivateMessageI;
interface InboxState { interface InboxState {
unreadOrAll: UnreadOrAll; unreadOrAll: UnreadOrAll;
unreadType: UnreadType; messageType: MessageType;
replies: Array<Comment>; replies: Array<Comment>;
mentions: Array<Comment>; mentions: Array<Comment>;
messages: Array<PrivateMessageI>; messages: Array<PrivateMessageI>;
@ -64,7 +62,7 @@ export class Inbox extends Component<any, InboxState> {
private subscription: Subscription; private subscription: Subscription;
private emptyState: InboxState = { private emptyState: InboxState = {
unreadOrAll: UnreadOrAll.Unread, unreadOrAll: UnreadOrAll.Unread,
unreadType: UnreadType.All, messageType: MessageType.All,
replies: [], replies: [],
mentions: [], mentions: [],
messages: [], messages: [],
@ -100,26 +98,19 @@ export class Inbox extends Component<any, InboxState> {
} }
render() { render() {
let user = UserService.Instance.user;
return ( return (
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<h5 class="mb-0"> <h5 class="mb-1">
<T {i18n.t('inbox')}
class="d-inline"
i18nKey="inbox_for"
interpolation={{ user: user.username }}
>
#<Link to={`/u/${user.username}`}>#</Link>
</T>
<small> <small>
<a <a
href={`/feeds/inbox/${UserService.Instance.auth}.xml`} href={`/feeds/inbox/${UserService.Instance.auth}.xml`}
target="_blank" target="_blank"
title="RSS" title="RSS"
> >
<svg class="icon mx-2 text-muted small"> <svg class="icon ml-2 text-muted small">
<use xlinkHref="#icon-rss">#</use> <use xlinkHref="#icon-rss">#</use>
</svg> </svg>
</a> </a>
@ -139,10 +130,10 @@ export class Inbox extends Component<any, InboxState> {
</ul> </ul>
)} )}
{this.selects()} {this.selects()}
{this.state.unreadType == UnreadType.All && this.all()} {this.state.messageType == MessageType.All && this.all()}
{this.state.unreadType == UnreadType.Replies && this.replies()} {this.state.messageType == MessageType.Replies && this.replies()}
{this.state.unreadType == UnreadType.Mentions && this.mentions()} {this.state.messageType == MessageType.Mentions && this.mentions()}
{this.state.unreadType == UnreadType.Messages && this.messages()} {this.state.messageType == MessageType.Messages && this.messages()}
{this.paginator()} {this.paginator()}
</div> </div>
</div> </div>
@ -150,29 +141,103 @@ export class Inbox extends Component<any, InboxState> {
); );
} }
unreadOrAllRadios() {
return (
<div class="btn-group btn-group-toggle">
<label
className={`btn btn-sm btn-secondary pointer
${this.state.unreadOrAll == UnreadOrAll.Unread && 'active'}
`}
>
<input
type="radio"
value={UnreadOrAll.Unread}
checked={this.state.unreadOrAll == UnreadOrAll.Unread}
onChange={linkEvent(this, this.handleUnreadOrAllChange)}
/>
{i18n.t('unread')}
</label>
<label
className={`btn btn-sm btn-secondary pointer
${this.state.unreadOrAll == UnreadOrAll.All && 'active'}
`}
>
<input
type="radio"
value={UnreadOrAll.All}
checked={this.state.unreadOrAll == UnreadOrAll.All}
onChange={linkEvent(this, this.handleUnreadOrAllChange)}
/>
{i18n.t('all')}
</label>
</div>
);
}
messageTypeRadios() {
return (
<div class="btn-group btn-group-toggle">
<label
className={`btn btn-sm btn-secondary pointer btn-outline-light
${this.state.messageType == MessageType.All && 'active'}
`}
>
<input
type="radio"
value={MessageType.All}
checked={this.state.messageType == MessageType.All}
onChange={linkEvent(this, this.handleMessageTypeChange)}
/>
{i18n.t('all')}
</label>
<label
className={`btn btn-sm btn-secondary pointer btn-outline-light
${this.state.messageType == MessageType.Replies && 'active'}
`}
>
<input
type="radio"
value={MessageType.Replies}
checked={this.state.messageType == MessageType.Replies}
onChange={linkEvent(this, this.handleMessageTypeChange)}
/>
{i18n.t('replies')}
</label>
<label
className={`btn btn-sm btn-secondary pointer btn-outline-light
${this.state.messageType == MessageType.Mentions && 'active'}
`}
>
<input
type="radio"
value={MessageType.Mentions}
checked={this.state.messageType == MessageType.Mentions}
onChange={linkEvent(this, this.handleMessageTypeChange)}
/>
{i18n.t('mentions')}
</label>
<label
className={`btn btn-sm btn-secondary pointer btn-outline-light
${this.state.messageType == MessageType.Messages && 'active'}
`}
>
<input
type="radio"
value={MessageType.Messages}
checked={this.state.messageType == MessageType.Messages}
onChange={linkEvent(this, this.handleMessageTypeChange)}
/>
{i18n.t('messages')}
</label>
</div>
);
}
selects() { selects() {
return ( return (
<div className="mb-2"> <div className="mb-2">
<select <span class="mr-3">{this.unreadOrAllRadios()}</span>
value={this.state.unreadOrAll} <span class="mr-3">{this.messageTypeRadios()}</span>
onChange={linkEvent(this, this.handleUnreadOrAllChange)}
class="custom-select custom-select-sm w-auto mr-2"
>
<option disabled>{i18n.t('type')}</option>
<option value={UnreadOrAll.Unread}>{i18n.t('unread')}</option>
<option value={UnreadOrAll.All}>{i18n.t('all')}</option>
</select>
<select
value={this.state.unreadType}
onChange={linkEvent(this, this.handleUnreadTypeChange)}
class="custom-select custom-select-sm w-auto mr-2"
>
<option disabled>{i18n.t('type')}</option>
<option value={UnreadType.All}>{i18n.t('all')}</option>
<option value={UnreadType.Replies}>{i18n.t('replies')}</option>
<option value={UnreadType.Mentions}>{i18n.t('mentions')}</option>
<option value={UnreadType.Messages}>{i18n.t('messages')}</option>
</select>
<SortSelect <SortSelect
sort={this.state.sort} sort={this.state.sort}
onChange={this.handleSortChange} onChange={this.handleSortChange}
@ -196,7 +261,12 @@ export class Inbox extends Component<any, InboxState> {
<div> <div>
{combined.map(i => {combined.map(i =>
isCommentType(i) ? ( isCommentType(i) ? (
<CommentNodes nodes={[{ comment: i }]} noIndent markable /> <CommentNodes
nodes={[{ comment: i }]}
noIndent
markable
showContext
/>
) : ( ) : (
<PrivateMessage privateMessage={i} /> <PrivateMessage privateMessage={i} />
) )
@ -212,6 +282,7 @@ export class Inbox extends Component<any, InboxState> {
nodes={commentsToFlatNodes(this.state.replies)} nodes={commentsToFlatNodes(this.state.replies)}
noIndent noIndent
markable markable
showContext
/> />
</div> </div>
); );
@ -221,7 +292,12 @@ export class Inbox extends Component<any, InboxState> {
return ( return (
<div> <div>
{this.state.mentions.map(mention => ( {this.state.mentions.map(mention => (
<CommentNodes nodes={[{ comment: mention }]} noIndent markable /> <CommentNodes
nodes={[{ comment: mention }]}
noIndent
markable
showContext
/>
))} ))}
</div> </div>
); );
@ -277,8 +353,8 @@ export class Inbox extends Component<any, InboxState> {
i.refetch(); i.refetch();
} }
handleUnreadTypeChange(i: Inbox, event: any) { handleMessageTypeChange(i: Inbox, event: any) {
i.state.unreadType = Number(event.target.value); i.state.messageType = Number(event.target.value);
i.state.page = 1; i.state.page = 1;
i.setState(i.state); i.setState(i.state);
i.refetch(); i.refetch();

View File

@ -423,6 +423,7 @@ export class Main extends Component<any, MainState> {
noIndent noIndent
showCommunity showCommunity
sortType={this.state.sort} sortType={this.state.sort}
showContext
/> />
); );
} }

View File

@ -26,6 +26,8 @@ import {
fetchLimit, fetchLimit,
isCommentType, isCommentType,
toast, toast,
messageToastify,
md,
} from '../utils'; } from '../utils';
import { version } from '../version'; import { version } from '../version';
import { i18n } from '../i18next'; import { i18n } from '../i18next';
@ -100,6 +102,22 @@ export class Navbar extends Component<any, NavbarState> {
<Link title={version} class="navbar-brand" to="/"> <Link title={version} class="navbar-brand" to="/">
{this.state.siteName} {this.state.siteName}
</Link> </Link>
{this.state.isLoggedIn && (
<Link
class="ml-auto p-0 navbar-toggler nav-link"
to="/inbox"
title={i18n.t('inbox')}
>
<svg class="icon">
<use xlinkHref="#icon-bell"></use>
</svg>
{this.state.unreadCount > 0 && (
<span class="ml-1 badge badge-light">
{this.state.unreadCount}
</span>
)}
</Link>
)}
<button <button
class="navbar-toggler" class="navbar-toggler"
type="button" type="button"
@ -350,21 +368,33 @@ export class Navbar extends Component<any, NavbarState> {
} }
notify(reply: Comment | PrivateMessage) { notify(reply: Comment | PrivateMessage) {
let creator_name = reply.creator_name;
let creator_avatar = reply.creator_avatar
? reply.creator_avatar
: `${window.location.protocol}//${window.location.host}/static/assets/apple-touch-icon.png`;
let link = isCommentType(reply)
? `/post/${reply.post_id}/comment/${reply.id}`
: `/inbox`;
let body = md.render(reply.content);
messageToastify(
creator_name,
creator_avatar,
body,
link,
this.context.router
);
if (Notification.permission !== 'granted') Notification.requestPermission(); if (Notification.permission !== 'granted') Notification.requestPermission();
else { else {
var notification = new Notification(reply.creator_name, { var notification = new Notification(reply.creator_name, {
icon: reply.creator_avatar icon: creator_avatar,
? reply.creator_avatar body: body,
: `${window.location.protocol}//${window.location.host}/static/assets/apple-touch-icon.png`,
body: `${reply.content}`,
}); });
notification.onclick = () => { notification.onclick = () => {
this.context.router.history.push( event.preventDefault();
isCommentType(reply) this.context.router.history.push(link);
? `/post/${reply.post_id}/comment/${reply.id}`
: `/inbox`
);
}; };
} }
} }

View File

@ -250,7 +250,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
<div class="row"> <div class="row">
<div className={`vote-bar col-1 pr-0 small text-center`}> <div className={`vote-bar col-1 pr-0 small text-center`}>
<button <button
className={`vote-animate btn btn-link p-0 ${ className={`btn-animate btn btn-link btn-lg p-0 ${
this.state.my_vote == 1 ? 'text-info' : 'text-muted' this.state.my_vote == 1 ? 'text-info' : 'text-muted'
}`} }`}
onClick={linkEvent(this, this.handlePostLike)} onClick={linkEvent(this, this.handlePostLike)}
@ -268,7 +268,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
</div> </div>
{WebSocketService.Instance.site.enable_downvotes && ( {WebSocketService.Instance.site.enable_downvotes && (
<button <button
className={`vote-animate btn btn-link p-0 ${ className={`btn-animate btn btn-link btn-lg p-0 ${
this.state.my_vote == -1 ? 'text-danger' : 'text-muted' this.state.my_vote == -1 ? 'text-danger' : 'text-muted'
}`} }`}
onClick={linkEvent(this, this.handlePostDisLike)} onClick={linkEvent(this, this.handlePostDisLike)}
@ -501,8 +501,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
</Link> </Link>
</li> </li>
</ul> </ul>
<ul class="list-inline mb-1 small text-muted">
{this.props.post.duplicates && ( {this.props.post.duplicates && (
<ul class="list-inline mb-1 small text-muted">
<> <>
<li className="list-inline-item mr-2"> <li className="list-inline-item mr-2">
{i18n.t('cross_posted_to')} {i18n.t('cross_posted_to')}
@ -515,16 +515,16 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
</li> </li>
))} ))}
</> </>
)}
</ul> </ul>
<ul class="list-inline mb-1 text-muted h5 font-weight-bold"> )}
<ul class="list-inline mb-1 text-muted font-weight-bold">
{UserService.Instance.user && ( {UserService.Instance.user && (
<> <>
{this.props.showBody && ( {this.props.showBody && (
<> <>
<li className="list-inline-item-action"> <li className="list-inline-item">
<span <button
class="pointer" class="btn btn-sm btn-link btn-animate text-muted"
onClick={linkEvent(this, this.handleSavePostClick)} onClick={linkEvent(this, this.handleSavePostClick)}
data-tippy-content={ data-tippy-content={
post.saved ? i18n.t('unsave') : i18n.t('save') post.saved ? i18n.t('unsave') : i18n.t('save')
@ -536,11 +536,11 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
> >
<use xlinkHref="#icon-star"></use> <use xlinkHref="#icon-star"></use>
</svg> </svg>
</span> </button>
</li> </li>
<li className="list-inline-item-action"> <li className="list-inline-item">
<Link <Link
className="text-muted" class="btn btn-sm btn-link btn-animate text-muted"
to={`/create_post${this.crossPostParams}`} to={`/create_post${this.crossPostParams}`}
title={i18n.t('cross_post')} title={i18n.t('cross_post')}
> >
@ -553,20 +553,20 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
)} )}
{this.myPost && this.props.showBody && ( {this.myPost && this.props.showBody && (
<> <>
<li className="list-inline-item-action"> <li className="list-inline-item">
<span <button
class="pointer" class="btn btn-sm btn-link btn-animate text-muted"
onClick={linkEvent(this, this.handleEditClick)} onClick={linkEvent(this, this.handleEditClick)}
data-tippy-content={i18n.t('edit')} data-tippy-content={i18n.t('edit')}
> >
<svg class="icon icon-inline"> <svg class="icon icon-inline">
<use xlinkHref="#icon-edit"></use> <use xlinkHref="#icon-edit"></use>
</svg> </svg>
</span> </button>
</li> </li>
<li className="list-inline-item-action"> <li className="list-inline-item">
<span <button
class="pointer" class="btn btn-sm btn-link btn-animate text-muted"
onClick={linkEvent(this, this.handleDeleteClick)} onClick={linkEvent(this, this.handleDeleteClick)}
data-tippy-content={ data-tippy-content={
!post.deleted !post.deleted
@ -580,29 +580,29 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
> >
<use xlinkHref="#icon-trash"></use> <use xlinkHref="#icon-trash"></use>
</svg> </svg>
</span> </button>
</li> </li>
</> </>
)} )}
{!this.state.showAdvanced && this.props.showBody ? ( {!this.state.showAdvanced && this.props.showBody ? (
<li className="list-inline-item-action"> <li className="list-inline-item">
<span <button
className="pointer" class="btn btn-sm btn-link btn-animate text-muted"
onClick={linkEvent(this, this.handleShowAdvanced)} onClick={linkEvent(this, this.handleShowAdvanced)}
data-tippy-content={i18n.t('more')} data-tippy-content={i18n.t('more')}
> >
<svg class="icon icon-inline"> <svg class="icon icon-inline">
<use xlinkHref="#icon-more-vertical"></use> <use xlinkHref="#icon-more-vertical"></use>
</svg> </svg>
</span> </button>
</li> </li>
) : ( ) : (
<> <>
{this.props.showBody && post.body && ( {this.props.showBody && post.body && (
<li className="list-inline-item-action"> <li className="list-inline-item">
<span <button
className="pointer" class="btn btn-sm btn-link btn-animate text-muted"
onClick={linkEvent(this, this.handleViewSource)} onClick={linkEvent(this, this.handleViewSource)}
data-tippy-content={i18n.t('view_source')} data-tippy-content={i18n.t('view_source')}
> >
@ -612,14 +612,14 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
> >
<use xlinkHref="#icon-file-text"></use> <use xlinkHref="#icon-file-text"></use>
</svg> </svg>
</span> </button>
</li> </li>
)} )}
{this.canModOnSelf && ( {this.canModOnSelf && (
<> <>
<li className="list-inline-item-action"> <li className="list-inline-item">
<span <button
class="pointer" class="btn btn-sm btn-link btn-animate text-muted"
onClick={linkEvent(this, this.handleModLock)} onClick={linkEvent(this, this.handleModLock)}
data-tippy-content={ data-tippy-content={
post.locked post.locked
@ -633,11 +633,11 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
> >
<use xlinkHref="#icon-lock"></use> <use xlinkHref="#icon-lock"></use>
</svg> </svg>
</span> </button>
</li> </li>
<li className="list-inline-item-action"> <li className="list-inline-item">
<span <button
class="pointer" class="btn btn-sm btn-link btn-animate text-muted"
onClick={linkEvent(this, this.handleModSticky)} onClick={linkEvent(this, this.handleModSticky)}
data-tippy-content={ data-tippy-content={
post.stickied post.stickied
@ -651,7 +651,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
> >
<use xlinkHref="#icon-pin"></use> <use xlinkHref="#icon-pin"></use>
</svg> </svg>
</span> </button>
</li> </li>
</> </>
)} )}

View File

@ -28,8 +28,7 @@ export class PostListings extends Component<PostListingsProps, any> {
post={post} post={post}
showCommunity={this.props.showCommunity} showCommunity={this.props.showCommunity}
/> />
<hr class="d-md-none my-2" /> <hr class="my-2" />
<div class="d-none d-md-block my-2"></div>
</> </>
)) ))
) : ( ) : (

View File

@ -211,7 +211,7 @@ export class Post extends Component<any, PostState> {
sortRadios() { sortRadios() {
return ( return (
<div class="btn-group btn-group-toggle"> <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 ${this.state
.commentSort === CommentSortType.Hot && 'active'}`} .commentSort === CommentSortType.Hot && 'active'}`}
@ -276,6 +276,7 @@ export class Post extends Component<any, PostState> {
moderators={this.state.moderators} moderators={this.state.moderators}
admins={this.state.admins} admins={this.state.admins}
postCreatorId={this.state.post.creator_id} postCreatorId={this.state.post.creator_id}
showContext
/> />
</div> </div>
</div> </div>

View File

@ -55,7 +55,7 @@ export class PrivateMessage extends Component<
render() { render() {
let message = this.props.privateMessage; let message = this.props.privateMessage;
return ( return (
<div class="mb-2"> <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">
<li className="list-inline-item"> <li className="list-inline-item">
@ -129,12 +129,12 @@ export class PrivateMessage extends Component<
dangerouslySetInnerHTML={mdToHtml(this.messageUnlessRemoved)} dangerouslySetInnerHTML={mdToHtml(this.messageUnlessRemoved)}
/> />
)} )}
<ul class="list-inline mb-1 text-muted h5 font-weight-bold"> <ul class="list-inline mb-0 text-muted font-weight-bold">
{!this.mine && ( {!this.mine && (
<> <>
<li className="list-inline-item-action"> <li className="list-inline-item">
<span <button
class="pointer" class="btn btn-link btn-sm btn-animate text-muted"
onClick={linkEvent(this, this.handleMarkRead)} onClick={linkEvent(this, this.handleMarkRead)}
data-tippy-content={ data-tippy-content={
message.read message.read
@ -148,37 +148,37 @@ export class PrivateMessage extends Component<
> >
<use xlinkHref="#icon-check"></use> <use xlinkHref="#icon-check"></use>
</svg> </svg>
</span> </button>
</li> </li>
<li className="list-inline-item-action"> <li className="list-inline-item">
<span <button
class="pointer" class="btn btn-link btn-sm btn-animate text-muted"
onClick={linkEvent(this, this.handleReplyClick)} onClick={linkEvent(this, this.handleReplyClick)}
data-tippy-content={i18n.t('reply')} data-tippy-content={i18n.t('reply')}
> >
<svg class="icon icon-inline"> <svg class="icon icon-inline">
<use xlinkHref="#icon-reply1"></use> <use xlinkHref="#icon-reply1"></use>
</svg> </svg>
</span> </button>
</li> </li>
</> </>
)} )}
{this.mine && ( {this.mine && (
<> <>
<li className="list-inline-item-action"> <li className="list-inline-item">
<span <button
class="pointer" class="btn btn-link btn-sm btn-animate text-muted"
onClick={linkEvent(this, this.handleEditClick)} onClick={linkEvent(this, this.handleEditClick)}
data-tippy-content={i18n.t('edit')} data-tippy-content={i18n.t('edit')}
> >
<svg class="icon icon-inline"> <svg class="icon icon-inline">
<use xlinkHref="#icon-edit"></use> <use xlinkHref="#icon-edit"></use>
</svg> </svg>
</span> </button>
</li> </li>
<li className="list-inline-item-action"> <li className="list-inline-item">
<span <button
class="pointer" class="btn btn-link btn-sm btn-animate text-muted"
onClick={linkEvent(this, this.handleDeleteClick)} onClick={linkEvent(this, this.handleDeleteClick)}
data-tippy-content={ data-tippy-content={
!message.deleted !message.deleted
@ -192,13 +192,13 @@ export class PrivateMessage extends Component<
> >
<use xlinkHref="#icon-trash"></use> <use xlinkHref="#icon-trash"></use>
</svg> </svg>
</span> </button>
</li> </li>
</> </>
)} )}
<li className="list-inline-item-action"> <li className="list-inline-item">
<span <button
className="pointer" class="btn btn-link btn-sm btn-animate text-muted"
onClick={linkEvent(this, this.handleViewSource)} onClick={linkEvent(this, this.handleViewSource)}
data-tippy-content={i18n.t('view_source')} data-tippy-content={i18n.t('view_source')}
> >
@ -208,7 +208,7 @@ export class PrivateMessage extends Component<
> >
<use xlinkHref="#icon-file-text"></use> <use xlinkHref="#icon-file-text"></use>
</svg> </svg>
</span> </button>
</li> </li>
</ul> </ul>
</div> </div>

View File

@ -88,14 +88,14 @@ export class Symbols extends Component<any, any> {
<path d="M17 19h-12c-0.553 0-1-0.447-1-1s0.447-1 1-1h12c0.553 0 1 0.447 1 1s-0.447 1-1 1z"></path> <path d="M17 19h-12c-0.553 0-1-0.447-1-1s0.447-1 1-1h12c0.553 0 1 0.447 1 1s-0.447 1-1 1z"></path>
<path d="M17.5 5h-12.5v9c0 1.1 0.9 2 2 2h8c1.1 0 2-0.9 2-2v-2h0.5c1.93 0 3.5-1.57 3.5-3.5s-1.57-3.5-3.5-3.5zM15 14h-8v-7h8v7zM17.5 10h-1.5v-3h1.5c0.827 0 1.5 0.673 1.5 1.5s-0.673 1.5-1.5 1.5z"></path> <path d="M17.5 5h-12.5v9c0 1.1 0.9 2 2 2h8c1.1 0 2-0.9 2-2v-2h0.5c1.93 0 3.5-1.57 3.5-3.5s-1.57-3.5-3.5-3.5zM15 14h-8v-7h8v7zM17.5 10h-1.5v-3h1.5c0.827 0 1.5 0.673 1.5 1.5s-0.673 1.5-1.5 1.5z"></path>
</symbol> </symbol>
<symbol id="icon-rss" viewBox="0 0 32 32"> <symbol id="icon-rss" viewBox="0 0 24 24">
<path d="M4.259 23.467c-2.35 0-4.259 1.917-4.259 4.252 0 2.349 1.909 4.244 4.259 4.244 2.358 0 4.265-1.895 4.265-4.244-0-2.336-1.907-4.252-4.265-4.252zM0.005 10.873v6.133c3.993 0 7.749 1.562 10.577 4.391 2.825 2.822 4.384 6.595 4.384 10.603h6.16c-0-11.651-9.478-21.127-21.121-21.127zM0.012 0v6.136c14.243 0 25.836 11.604 25.836 25.864h6.152c0-17.64-14.352-32-31.988-32z"></path> <path d="M4 12c2.209 0 4.208 0.894 5.657 2.343s2.343 3.448 2.343 5.657c0 0.552 0.448 1 1 1s1-0.448 1-1c0-2.761-1.12-5.263-2.929-7.071s-4.31-2.929-7.071-2.929c-0.552 0-1 0.448-1 1s0.448 1 1 1zM4 5c4.142 0 7.891 1.678 10.607 4.393s4.393 6.465 4.393 10.607c0 0.552 0.448 1 1 1s1-0.448 1-1c0-4.694-1.904-8.946-4.979-12.021s-7.327-4.979-12.021-4.979c-0.552 0-1 0.448-1 1s0.448 1 1 1zM7 19c0-0.552-0.225-1.053-0.586-1.414s-0.862-0.586-1.414-0.586-1.053 0.225-1.414 0.586-0.586 0.862-0.586 1.414 0.225 1.053 0.586 1.414 0.862 0.586 1.414 0.586 1.053-0.225 1.414-0.586 0.586-0.862 0.586-1.414z"></path>
</symbol> </symbol>
<symbol id="icon-arrow-down" viewBox="0 0 26 28"> <symbol id="icon-arrow-down" viewBox="0 0 24 24">
<path d="M25.172 13c0 0.531-0.219 1.047-0.578 1.406l-10.172 10.187c-0.375 0.359-0.891 0.578-1.422 0.578s-1.047-0.219-1.406-0.578l-10.172-10.187c-0.375-0.359-0.594-0.875-0.594-1.406s0.219-1.047 0.594-1.422l1.156-1.172c0.375-0.359 0.891-0.578 1.422-0.578s1.047 0.219 1.406 0.578l4.594 4.594v-11c0-1.094 0.906-2 2-2h2c1.094 0 2 0.906 2 2v11l4.594-4.594c0.359-0.359 0.875-0.578 1.406-0.578s1.047 0.219 1.422 0.578l1.172 1.172c0.359 0.375 0.578 0.891 0.578 1.422z"></path> <path d="M18.293 11.293l-5.293 5.293v-11.586c0-0.552-0.448-1-1-1s-1 0.448-1 1v11.586l-5.293-5.293c-0.391-0.391-1.024-0.391-1.414 0s-0.391 1.024 0 1.414l7 7c0.092 0.092 0.202 0.166 0.324 0.217 0.245 0.101 0.521 0.101 0.766 0 0.118-0.049 0.228-0.121 0.324-0.217l7-7c0.391-0.391 0.391-1.024 0-1.414s-1.024-0.391-1.414 0z"></path>
</symbol> </symbol>
<symbol id="icon-arrow-up" viewBox="0 0 26 28"> <symbol id="icon-arrow-up" viewBox="0 0 24 24">
<path d="M25.172 15.172c0 0.531-0.219 1.031-0.578 1.406l-1.172 1.172c-0.375 0.375-0.891 0.594-1.422 0.594s-1.047-0.219-1.406-0.594l-4.594-4.578v11c0 1.125-0.938 1.828-2 1.828h-2c-1.062 0-2-0.703-2-1.828v-11l-4.594 4.578c-0.359 0.375-0.875 0.594-1.406 0.594s-1.047-0.219-1.406-0.594l-1.172-1.172c-0.375-0.375-0.594-0.875-0.594-1.406s0.219-1.047 0.594-1.422l10.172-10.172c0.359-0.375 0.875-0.578 1.406-0.578s1.047 0.203 1.422 0.578l10.172 10.172c0.359 0.375 0.578 0.891 0.578 1.422z"></path> <path d="M5.707 12.707l5.293-5.293v11.586c0 0.552 0.448 1 1 1s1-0.448 1-1v-11.586l5.293 5.293c0.391 0.391 1.024 0.391 1.414 0s0.391-1.024 0-1.414l-7-7c-0.092-0.092-0.202-0.166-0.324-0.217s-0.253-0.076-0.383-0.076c-0.256 0-0.512 0.098-0.707 0.293l-7 7c-0.391 0.391-0.391 1.024 0 1.414s1.024 0.391 1.414 0z"></path>
</symbol> </symbol>
<symbol id="icon-mail" viewBox="0 0 24 24"> <symbol id="icon-mail" viewBox="0 0 24 24">
<path d="M3 7.921l8.427 5.899c0.34 0.235 0.795 0.246 1.147 0l8.426-5.899v10.079c0 0.272-0.11 0.521-0.295 0.705s-0.433 0.295-0.705 0.295h-16c-0.272 0-0.521-0.11-0.705-0.295s-0.295-0.433-0.295-0.705zM1 5.983c0 0.010 0 0.020 0 0.030v11.987c0 0.828 0.34 1.579 0.88 2.12s1.292 0.88 2.12 0.88h16c0.828 0 1.579-0.34 2.12-0.88s0.88-1.292 0.88-2.12v-11.988c0-0.010 0-0.020 0-0.030-0.005-0.821-0.343-1.565-0.88-2.102-0.541-0.54-1.292-0.88-2.12-0.88h-16c-0.828 0-1.579 0.34-2.12 0.88-0.537 0.537-0.875 1.281-0.88 2.103zM20.894 5.554l-8.894 6.225-8.894-6.225c0.048-0.096 0.112-0.183 0.188-0.259 0.185-0.185 0.434-0.295 0.706-0.295h16c0.272 0 0.521 0.11 0.705 0.295 0.076 0.076 0.14 0.164 0.188 0.259z"></path> <path d="M3 7.921l8.427 5.899c0.34 0.235 0.795 0.246 1.147 0l8.426-5.899v10.079c0 0.272-0.11 0.521-0.295 0.705s-0.433 0.295-0.705 0.295h-16c-0.272 0-0.521-0.11-0.705-0.295s-0.295-0.433-0.295-0.705zM1 5.983c0 0.010 0 0.020 0 0.030v11.987c0 0.828 0.34 1.579 0.88 2.12s1.292 0.88 2.12 0.88h16c0.828 0 1.579-0.34 2.12-0.88s0.88-1.292 0.88-2.12v-11.988c0-0.010 0-0.020 0-0.030-0.005-0.821-0.343-1.565-0.88-2.102-0.541-0.54-1.292-0.88-2.12-0.88h-16c-0.828 0-1.579 0.34-2.12 0.88-0.537 0.537-0.875 1.281-0.88 2.103zM20.894 5.554l-8.894 6.225-8.894-6.225c0.048-0.096 0.112-0.183 0.188-0.259 0.185-0.185 0.434-0.295 0.706-0.295h16c0.272 0 0.521 0.11 0.705 0.295 0.076 0.076 0.14 0.164 0.188 0.259z"></path>

View File

@ -242,27 +242,74 @@ export class User extends Component<any, UserState> {
); );
} }
viewRadios() {
return (
<div class="btn-group btn-group-toggle">
<label
className={`btn btn-sm btn-secondary pointer btn-outline-light
${this.state.view == View.Overview && 'active'}
`}
>
<input
type="radio"
value={View.Overview}
checked={this.state.view == View.Overview}
onChange={linkEvent(this, this.handleViewChange)}
/>
{i18n.t('overview')}
</label>
<label
className={`btn btn-sm btn-secondary pointer btn-outline-light
${this.state.view == View.Comments && 'active'}
`}
>
<input
type="radio"
value={View.Comments}
checked={this.state.view == View.Comments}
onChange={linkEvent(this, this.handleViewChange)}
/>
{i18n.t('comments')}
</label>
<label
className={`btn btn-sm btn-secondary pointer btn-outline-light
${this.state.view == View.Posts && 'active'}
`}
>
<input
type="radio"
value={View.Posts}
checked={this.state.view == View.Posts}
onChange={linkEvent(this, this.handleViewChange)}
/>
{i18n.t('posts')}
</label>
<label
className={`btn btn-sm btn-secondary pointer btn-outline-light
${this.state.view == View.Saved && 'active'}
`}
>
<input
type="radio"
value={View.Saved}
checked={this.state.view == View.Saved}
onChange={linkEvent(this, this.handleViewChange)}
/>
{i18n.t('saved')}
</label>
</div>
);
}
selects() { selects() {
return ( return (
<div className="mb-2"> <div className="mb-2">
<select <span class="mr-3">{this.viewRadios()}</span>
value={this.state.view}
onChange={linkEvent(this, this.handleViewChange)}
class="custom-select custom-select-sm w-auto"
>
<option disabled>{i18n.t('view')}</option>
<option value={View.Overview}>{i18n.t('overview')}</option>
<option value={View.Comments}>{i18n.t('comments')}</option>
<option value={View.Posts}>{i18n.t('posts')}</option>
<option value={View.Saved}>{i18n.t('saved')}</option>
</select>
<span class="ml-2">
<SortSelect <SortSelect
sort={this.state.sort} sort={this.state.sort}
onChange={this.handleSortChange} onChange={this.handleSortChange}
hideHot hideHot
/> />
</span>
<a <a
href={`/feeds/u/${this.state.username}.xml?sort=${ href={`/feeds/u/${this.state.username}.xml?sort=${
SortType[this.state.sort] SortType[this.state.sort]
@ -312,6 +359,7 @@ export class User extends Component<any, UserState> {
nodes={[{ comment: i.data as Comment }]} nodes={[{ comment: i.data as Comment }]}
admins={this.state.admins} admins={this.state.admins}
noIndent noIndent
showContext
/> />
)} )}
</div> </div>
@ -327,6 +375,7 @@ export class User extends Component<any, UserState> {
nodes={commentsToFlatNodes(this.state.comments)} nodes={commentsToFlatNodes(this.state.comments)}
admins={this.state.admins} admins={this.state.admins}
noIndent noIndent
showContext
/> />
</div> </div>
); );

28
ui/src/utils.ts vendored
View File

@ -218,7 +218,7 @@ export function validURL(str: string) {
} }
export function validEmail(email: string) { export function validEmail(email: string) {
let re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; let re = /^(([^\s"(),.:;<>@[\\\]]+(\.[^\s"(),.:;<>@[\\\]]+)*)|(".+"))@((\[(?:\d{1,3}\.){3}\d{1,3}])|(([\dA-Za-z\-]+\.)+[A-Za-z]{2,}))$/;
return re.test(String(email).toLowerCase()); return re.test(String(email).toLowerCase());
} }
@ -436,6 +436,32 @@ export function toast(text: string, background: string = 'success') {
}).showToast(); }).showToast();
} }
export function messageToastify(
creator: string,
avatar: string,
body: string,
link: string,
router: any
) {
let backgroundColor = `var(--light)`;
let toast = Toastify({
text: `${body}<br />${creator}`,
avatar: avatar,
backgroundColor: backgroundColor,
close: true,
gravity: 'top',
position: 'right',
duration: 0,
onClick: () => {
if (toast) {
toast.hideToast();
router.history.push(link);
}
},
}).showToast();
}
export function setupTribute(): Tribute { export function setupTribute(): Tribute {
return new Tribute({ return new Tribute({
collection: [ collection: [

2
ui/src/version.ts vendored
View File

@ -1 +1 @@
export const version: string = 'v0.6.39'; export const version: string = 'v0.6.44';

View File

@ -39,6 +39,7 @@
"avatar": "Avatar", "avatar": "Avatar",
"upload_avatar": "Upload Avatar", "upload_avatar": "Upload Avatar",
"show_avatars": "Show Avatars", "show_avatars": "Show Avatars",
"show_context": "Show context",
"formatting_help": "formatting help", "formatting_help": "formatting help",
"sorting_help": "sorting help", "sorting_help": "sorting help",
"view_source": "view source", "view_source": "view source",

View File

@ -198,5 +198,7 @@
"upvote": "賛成票", "upvote": "賛成票",
"downvote": "反対票", "downvote": "反対票",
"sorting_help": "並び順のヘルプ", "sorting_help": "並び順のヘルプ",
"block_leaving": "このページから離れてもよろしいですか?" "block_leaving": "このページから離れてもよろしいですか?",
"number_of_upvotes": "{{count}} 票の賛成",
"number_of_downvotes": "{{count}} 票の反対"
} }

3
ui/translations/pl.json vendored 100644
View File

@ -0,0 +1,3 @@
{
}

View File

@ -239,5 +239,9 @@
"upvote": "Voto positivo", "upvote": "Voto positivo",
"downvote": "Voto negativo", "downvote": "Voto negativo",
"block_leaving": "Tem certeza que quer sair?", "block_leaving": "Tem certeza que quer sair?",
"sorting_help": "ajuda sobre ordenação" "sorting_help": "ajuda sobre ordenação",
"number_of_upvotes": "{{count}} voto positivo",
"number_of_upvotes_plural": "{{count}} votos positivos",
"number_of_downvotes": "{{count}} voto negativo",
"number_of_downvotes_plural": "{{count}} votos negativos"
} }

View File

@ -4,11 +4,15 @@
"no_posts": "Нет записей.", "no_posts": "Нет записей.",
"create_a_post": "Создать запись", "create_a_post": "Создать запись",
"create_post": "Создать запись", "create_post": "Создать запись",
"number_of_posts": "{{count}} записей", "number_of_posts_0": "{{count}} запись",
"number_of_posts_1": "{{count}} записи",
"number_of_posts_2": "{{count}} записей",
"posts": "Записи", "posts": "Записи",
"related_posts": "Эти записи могут быть связаны", "related_posts": "Эти записи могут быть связаны",
"comments": "Комментарии", "comments": "Комментарии",
"number_of_comments": "{{count}} комментариев", "number_of_comments_0": "{{count}} комментарий",
"number_of_comments_1": "{{count}} комментария",
"number_of_comments_2": "{{count}} комментариев",
"remove_comment": "Удалить комментарий", "remove_comment": "Удалить комментарий",
"communities": "Сообщества", "communities": "Сообщества",
"users": "Пользователи", "users": "Пользователи",
@ -169,14 +173,14 @@
"sticky": "", "sticky": "",
"stickied": "закрепленный пост", "stickied": "закрепленный пост",
"delete_account": "Удалить аккаунт", "delete_account": "Удалить аккаунт",
"delete_account_confirm": "Предупреждение: это действите полностью уничтожит все данные вашего аккаунта. Введите свой пароль для подтверждения.", "delete_account_confirm": "Предупреждение: это действие полностью уничтожит все данные вашего аккаунта. Введите свой пароль для подтверждения.",
"docs": "Документация", "docs": "Документация",
"replies": "Ответы", "replies": "Ответы",
"mentions": "Упоминания", "mentions": "Упоминания",
"message_sent": "Сообщение отправлено", "message_sent": "Сообщение отправлено",
"old_password": "Действующий пароль", "old_password": "Действующий пароль",
"forgot_password": "я забыл(а) пароль", "forgot_password": "я забыл(а) пароль",
"reset_password_mail_sent": "Имейл для восстановления пароля был выслан.", "reset_password_mail_sent": "Письмо для восстановления пароля было выслано.",
"private_message_disclaimer": "Предупреждение: Приватные сообщения Lemmy на данный момент не зашифрованы. Для безопасной коммуникации создайте аккаунт на <1>Riot.im</1>.", "private_message_disclaimer": "Предупреждение: Приватные сообщения Lemmy на данный момент не зашифрованы. Для безопасной коммуникации создайте аккаунт на <1>Riot.im</1>.",
"send_notifications_to_email": "Посылать уведомления на e-mail адрес", "send_notifications_to_email": "Посылать уведомления на e-mail адрес",
"language": "Язык", "language": "Язык",
@ -200,5 +204,15 @@
"theme": "Визуальная тема", "theme": "Визуальная тема",
"post_title_too_long": "Длина названия поста превышает допустимый лимит.", "post_title_too_long": "Длина названия поста превышает допустимый лимит.",
"time": "Время", "time": "Время",
"action": "Действие" "action": "Действие",
"view_source": "исходный код сообщения",
"more": "больше",
"sorting_help": "справка по сортировке",
"by": "от",
"number_of_communities_0": "{{count}} сообщество",
"number_of_communities_1": "{{count}} сообщества",
"number_of_communities_2": "{{count}} сообществ",
"creator": "автор",
"old": "Старое",
"to": "в"
} }

392
ui/yarn.lock vendored
View File

@ -9,12 +9,12 @@
dependencies: dependencies:
"@babel/highlight" "^7.8.3" "@babel/highlight" "^7.8.3"
"@babel/generator@^7.8.4": "@babel/generator@^7.8.6":
version "7.8.4" version "7.8.8"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.8.4.tgz#35bbc74486956fe4251829f9f6c48330e8d0985e" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.8.8.tgz#cdcd58caab730834cee9eeadb729e833b625da3e"
integrity sha512-PwhclGdRpNAf3IxZb0YVuITPZmmrXz9zf6fH8lT4XbrmfQKr6ryBzhv593P5C6poJRciFCL/eHGW2NuGrgEyxA== integrity sha512-HKyUVu69cZoclptr8t8U5b6sx6zoWjh8jiUhnuj3MpZuKT2dJ8zPTuiy31luq32swhI0SpwItCIlU8XW7BZeJg==
dependencies: dependencies:
"@babel/types" "^7.8.3" "@babel/types" "^7.8.7"
jsesc "^2.5.1" jsesc "^2.5.1"
lodash "^4.17.13" lodash "^4.17.13"
source-map "^0.5.0" source-map "^0.5.0"
@ -51,7 +51,12 @@
esutils "^2.0.2" esutils "^2.0.2"
js-tokens "^4.0.0" js-tokens "^4.0.0"
"@babel/parser@^7.0.0", "@babel/parser@^7.8.3", "@babel/parser@^7.8.4": "@babel/parser@^7.7.0", "@babel/parser@^7.8.6":
version "7.8.8"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.8.tgz#4c3b7ce36db37e0629be1f0d50a571d2f86f6cd4"
integrity sha512-mO5GWzBPsPf6865iIbzNE0AvkKF3NE+2S3eRUpE+FE07BOAkXh6G+GW/Pj01hhXjve1WScbaIO4UlY1JKeqCcA==
"@babel/parser@^7.8.3":
version "7.8.4" version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.4.tgz#d1dbe64691d60358a974295fa53da074dd2ce8e8" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.4.tgz#d1dbe64691d60358a974295fa53da074dd2ce8e8"
integrity sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw== integrity sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==
@ -64,6 +69,14 @@
core-js-pure "^3.0.0" core-js-pure "^3.0.0"
regenerator-runtime "^0.13.2" regenerator-runtime "^0.13.2"
"@babel/runtime-corejs3@^7.8.3":
version "7.8.7"
resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.8.7.tgz#8209d9dff2f33aa2616cb319c83fe159ffb07b8c"
integrity sha512-sc7A+H4I8kTd7S61dgB9RomXu/C+F4IrRr4Ytze4dnfx7AXEpCrejSNpjx7vq6y/Bak9S6Kbk65a/WgMLtg43Q==
dependencies:
core-js-pure "^3.0.0"
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.1.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.4": "@babel/runtime@^7.1.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.4":
version "7.8.4" version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.4.tgz#d79f5a2040f7caa24d53e563aad49cbc05581308" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.4.tgz#d79f5a2040f7caa24d53e563aad49cbc05581308"
@ -80,22 +93,31 @@
"@babel/parser" "^7.8.3" "@babel/parser" "^7.8.3"
"@babel/types" "^7.8.3" "@babel/types" "^7.8.3"
"@babel/traverse@^7.0.0": "@babel/traverse@^7.7.0":
version "7.8.4" version "7.8.6"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.8.4.tgz#f0845822365f9d5b0e312ed3959d3f827f869e3c" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.8.6.tgz#acfe0c64e1cd991b3e32eae813a6eb564954b5ff"
integrity sha512-NGLJPZwnVEyBPLI+bl9y9aSnxMhsKz42so7ApAv9D+b4vAFPpY013FTS9LdKxcABoIYFU52HcYga1pPlx454mg== integrity sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A==
dependencies: dependencies:
"@babel/code-frame" "^7.8.3" "@babel/code-frame" "^7.8.3"
"@babel/generator" "^7.8.4" "@babel/generator" "^7.8.6"
"@babel/helper-function-name" "^7.8.3" "@babel/helper-function-name" "^7.8.3"
"@babel/helper-split-export-declaration" "^7.8.3" "@babel/helper-split-export-declaration" "^7.8.3"
"@babel/parser" "^7.8.4" "@babel/parser" "^7.8.6"
"@babel/types" "^7.8.3" "@babel/types" "^7.8.6"
debug "^4.1.0" debug "^4.1.0"
globals "^11.1.0" globals "^11.1.0"
lodash "^4.17.13" lodash "^4.17.13"
"@babel/types@^7.0.0", "@babel/types@^7.8.3": "@babel/types@^7.7.0", "@babel/types@^7.8.6", "@babel/types@^7.8.7":
version "7.8.7"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.8.7.tgz#1fc9729e1acbb2337d5b6977a63979b4819f5d1d"
integrity sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==
dependencies:
esutils "^2.0.2"
lodash "^4.17.13"
to-fast-properties "^2.0.0"
"@babel/types@^7.8.3":
version "7.8.3" version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.8.3.tgz#5a383dffa5416db1b73dedffd311ffd0788fb31c" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.8.3.tgz#5a383dffa5416db1b73dedffd311ffd0788fb31c"
integrity sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg== integrity sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==
@ -104,10 +126,10 @@
lodash "^4.17.13" lodash "^4.17.13"
to-fast-properties "^2.0.0" to-fast-properties "^2.0.0"
"@popperjs/core@^2.0.6": "@popperjs/core@^2.1.1":
version "2.0.6" version "2.1.1"
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.0.6.tgz#5a39ac118811ca844155b0ad5190b8c24f35e533" resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.1.1.tgz#12c572ab88ef7345b43f21883fca26631c223085"
integrity sha512-zj7Gw8QC4jmR92eKUvtrZUEpl2ypRbq+qlE4pwf9n2hnUO9BOAcWUs4/Ht+gNIbFt98xtqhLvccdCfD469MzpQ== integrity sha512-sLqWxCzC5/QHLhziXSCAksBxHfOnQlhPRVgPK0egEw+ktWvG75T2k+aYWVjVh9+WKeT3tlG3ZNbZQvZLmfuOIw==
"@samverschueren/stream-to-observable@^0.3.0": "@samverschueren/stream-to-observable@^0.3.0":
version "0.3.0" version "0.3.0"
@ -140,10 +162,10 @@
dependencies: dependencies:
"@types/sizzle" "*" "@types/sizzle" "*"
"@types/js-cookie@^2.2.1": "@types/js-cookie@^2.2.5":
version "2.2.4" version "2.2.5"
resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.2.4.tgz#f79720b4755aa197c2e15e982e2f438f5748e348" resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.2.5.tgz#38dfaacae8623b37cc0b0d27398e574e3fc28b1e"
integrity sha512-WTfSE1Eauak/Nrg6cA9FgPTFvVawejsai6zXoq0QYTQ3mxONeRtGhKxa7wMlUzWWmzrmTeV+rwLjHgsCntdrsA== integrity sha512-cpmwBRcHJmmZx0OGU7aPVwGWGbs4iKwVYchk9iuMtxNCA2zorwdaTz4GkLgs2WGxiRZRFKnV1k6tRUHX7tBMxg==
"@types/json-schema@^7.0.3": "@types/json-schema@^7.0.3":
version "7.0.4" version "7.0.4"
@ -174,10 +196,10 @@
dependencies: dependencies:
"@types/linkify-it" "*" "@types/linkify-it" "*"
"@types/node@^13.7.0": "@types/node@^13.9.2":
version "13.7.0" version "13.9.2"
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.7.0.tgz#b417deda18cf8400f278733499ad5547ed1abec4" resolved "https://registry.yarnpkg.com/@types/node/-/node-13.9.2.tgz#ace1880c03594cc3e80206d96847157d8e7fa349"
integrity sha512-GnZbirvmqZUzMgkFn70c74OQpTTUcCzlhQliTzYjQMqg+hVKcDnxdL19Ne3UdYzdMA/+W3eb646FWn/ZaT1NfQ== integrity sha512-bnoqK579sAYrQbp73wwglccjJ4sfRdKU7WNEZ5FW4K2U6Kc0/eZ5kvXG0JKsEKFB50zrFmfFt52/cvBbZa7eXg==
"@types/normalize-package-data@^2.4.0": "@types/normalize-package-data@^2.4.0":
version "2.4.0" version "2.4.0"
@ -194,18 +216,27 @@
resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47" resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47"
integrity sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg== integrity sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==
"@typescript-eslint/eslint-plugin@2.18.0": "@typescript-eslint/eslint-plugin@2.24.0":
version "2.18.0" version "2.24.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.18.0.tgz#f8cf272dfb057ecf1ea000fea1e0b3f06a32f9cb" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.24.0.tgz#a86cf618c965a462cddf3601f594544b134d6d68"
integrity sha512-kuO8WQjV+RCZvAXVRJfXWiJ8iYEtfHlKgcqqqXg9uUkIolEHuUaMmm8/lcO4xwCOtaw6mY0gStn2Lg4/eUXXYQ== integrity sha512-wJRBeaMeT7RLQ27UQkDFOu25MqFOBus8PtOa9KaT5ZuxC1kAsd7JEHqWt4YXuY9eancX0GK9C68i5OROnlIzBA==
dependencies: dependencies:
"@typescript-eslint/experimental-utils" "2.18.0" "@typescript-eslint/experimental-utils" "2.24.0"
eslint-utils "^1.4.3" eslint-utils "^1.4.3"
functional-red-black-tree "^1.0.1" functional-red-black-tree "^1.0.1"
regexpp "^3.0.0" regexpp "^3.0.0"
tsutils "^3.17.1" tsutils "^3.17.1"
"@typescript-eslint/experimental-utils@2.18.0", "@typescript-eslint/experimental-utils@^2.5.0": "@typescript-eslint/experimental-utils@2.24.0":
version "2.24.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.24.0.tgz#a5cb2ed89fedf8b59638dc83484eb0c8c35e1143"
integrity sha512-DXrwuXTdVh3ycNCMYmWhUzn/gfqu9N0VzNnahjiDJvcyhfBy4gb59ncVZVxdp5XzBC77dCncu0daQgOkbvPwBw==
dependencies:
"@types/json-schema" "^7.0.3"
"@typescript-eslint/typescript-estree" "2.24.0"
eslint-scope "^5.0.0"
"@typescript-eslint/experimental-utils@^2.5.0":
version "2.18.0" version "2.18.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.18.0.tgz#e4eab839082030282496c1439bbf9fdf2a4f3da8" resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.18.0.tgz#e4eab839082030282496c1439bbf9fdf2a4f3da8"
integrity sha512-J6MopKPHuJYmQUkANLip7g9I82ZLe1naCbxZZW3O2sIxTiq/9YYoOELEKY7oPg0hJ0V/AQ225h2z0Yp+RRMXhw== integrity sha512-J6MopKPHuJYmQUkANLip7g9I82ZLe1naCbxZZW3O2sIxTiq/9YYoOELEKY7oPg0hJ0V/AQ225h2z0Yp+RRMXhw==
@ -214,14 +245,14 @@
"@typescript-eslint/typescript-estree" "2.18.0" "@typescript-eslint/typescript-estree" "2.18.0"
eslint-scope "^5.0.0" eslint-scope "^5.0.0"
"@typescript-eslint/parser@2.18.0": "@typescript-eslint/parser@2.24.0":
version "2.18.0" version "2.24.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.18.0.tgz#d5f7fc1839abd4a985394e40e9d2454bd56aeb1f" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.24.0.tgz#2cf0eae6e6dd44d162486ad949c126b887f11eb8"
integrity sha512-SJJPxFMEYEWkM6pGfcnjLU+NJIPo+Ko1QrCBL+i0+zV30ggLD90huEmMMhKLHBpESWy9lVEeWlQibweNQzyc+A== integrity sha512-H2Y7uacwSSg8IbVxdYExSI3T7uM1DzmOn2COGtCahCC3g8YtM1xYAPi2MAHyfPs61VKxP/J/UiSctcRgw4G8aw==
dependencies: dependencies:
"@types/eslint-visitor-keys" "^1.0.0" "@types/eslint-visitor-keys" "^1.0.0"
"@typescript-eslint/experimental-utils" "2.18.0" "@typescript-eslint/experimental-utils" "2.24.0"
"@typescript-eslint/typescript-estree" "2.18.0" "@typescript-eslint/typescript-estree" "2.24.0"
eslint-visitor-keys "^1.1.0" eslint-visitor-keys "^1.1.0"
"@typescript-eslint/typescript-estree@2.18.0": "@typescript-eslint/typescript-estree@2.18.0":
@ -237,6 +268,19 @@
semver "^6.3.0" semver "^6.3.0"
tsutils "^3.17.1" tsutils "^3.17.1"
"@typescript-eslint/typescript-estree@2.24.0":
version "2.24.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.24.0.tgz#38bbc8bb479790d2f324797ffbcdb346d897c62a"
integrity sha512-RJ0yMe5owMSix55qX7Mi9V6z2FDuuDpN6eR5fzRJrp+8in9UF41IGNQHbg5aMK4/PjVaEQksLvz0IA8n+Mr/FA==
dependencies:
debug "^4.1.1"
eslint-visitor-keys "^1.1.0"
glob "^7.1.6"
is-glob "^4.0.1"
lodash "^4.17.15"
semver "^6.3.0"
tsutils "^3.17.1"
accepts@~1.3.7: accepts@~1.3.7:
version "1.3.7" version "1.3.7"
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd"
@ -504,15 +548,15 @@ axobject-query@^2.0.2:
"@babel/runtime" "^7.7.4" "@babel/runtime" "^7.7.4"
"@babel/runtime-corejs3" "^7.7.4" "@babel/runtime-corejs3" "^7.7.4"
babel-eslint@10.0.3: babel-eslint@10.1.0:
version "10.0.3" version "10.1.0"
resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.3.tgz#81a2c669be0f205e19462fed2482d33e4687a88a" resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232"
integrity sha512-z3U7eMY6r/3f3/JB9mTsLjyxrv0Yb1zb8PCWCLpguxfCzBIZUwy23R1t/XKewP+8mEN2Ck8Dtr4q20z6ce6SoA== integrity sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==
dependencies: dependencies:
"@babel/code-frame" "^7.0.0" "@babel/code-frame" "^7.0.0"
"@babel/parser" "^7.0.0" "@babel/parser" "^7.7.0"
"@babel/traverse" "^7.0.0" "@babel/traverse" "^7.7.0"
"@babel/types" "^7.0.0" "@babel/types" "^7.7.0"
eslint-visitor-keys "^1.0.0" eslint-visitor-keys "^1.0.0"
resolve "^1.12.0" resolve "^1.12.0"
@ -1207,10 +1251,10 @@ eslint-plugin-es@^3.0.0:
eslint-utils "^2.0.0" eslint-utils "^2.0.0"
regexpp "^3.0.0" regexpp "^3.0.0"
eslint-plugin-import@2.20.0: eslint-plugin-import@2.20.1:
version "2.20.0" version "2.20.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.20.0.tgz#d749a7263fb6c29980def8e960d380a6aa6aecaa" resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.20.1.tgz#802423196dcb11d9ce8435a5fc02a6d3b46939b3"
integrity sha512-NK42oA0mUc8Ngn4kONOPsPB1XhbUvNHqF+g307dPV28aknPoiNnKLFd9em4nkswwepdF5ouieqv5Th/63U7YJQ== integrity sha512-qQHgFOTjguR+LnYRoToeZWT62XM55MBVXObHM6SKFd1VzDcX/vqT1kAz8ssqigh5eMj8qXcRoXXGZpPP6RfdCw==
dependencies: dependencies:
array-includes "^3.0.3" array-includes "^3.0.3"
array.prototype.flat "^1.2.1" array.prototype.flat "^1.2.1"
@ -1239,33 +1283,32 @@ 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.0.2: eslint-plugin-jane@^7.2.0:
version "7.1.0" version "7.2.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-jane/-/eslint-plugin-jane-7.1.0.tgz#ee087405329e6bc9bfe9316fc5881c1d4e27bc71" resolved "https://registry.yarnpkg.com/eslint-plugin-jane/-/eslint-plugin-jane-7.2.0.tgz#a2454a6700c644e6c86821ca294adf303e75eddc"
integrity sha512-ScsxkkeTUnGYKLaiIk5zz/x7ZkDh7+rTj94daZboNmkJejdYka0sLFpfvDGm/7B8ImKacKdjRatQD0HjxlaPzA== integrity sha512-/BPZrfxWX9T45gJSf4/2GHfBYgsBYTW7StAQfxL8PxWABZIQKWPWy/5ZokX7UaJlgKHAoC42rJHCQLK5hmfJNA==
dependencies: dependencies:
"@typescript-eslint/eslint-plugin" "2.18.0" "@typescript-eslint/eslint-plugin" "2.24.0"
"@typescript-eslint/parser" "2.18.0" "@typescript-eslint/parser" "2.24.0"
babel-eslint "10.0.3" babel-eslint "10.1.0"
eslint-config-prettier "6.10.0" eslint-config-prettier "6.10.0"
eslint-plugin-babel "5.3.0" eslint-plugin-babel "5.3.0"
eslint-plugin-import "2.20.0" eslint-plugin-import "2.20.1"
eslint-plugin-jest "23.6.0" eslint-plugin-jest "23.8.2"
eslint-plugin-jsx-a11y "6.2.3" eslint-plugin-jsx-a11y "6.2.3"
eslint-plugin-node "11.0.0" eslint-plugin-node "11.0.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.18.0" eslint-plugin-react "7.19.0"
eslint-plugin-react-hooks "2.3.0" eslint-plugin-react-hooks "2.5.0"
eslint-plugin-unicorn "15.0.1" eslint-plugin-unicorn "17.2.0"
eslint-plugin-jest@23.6.0: eslint-plugin-jest@23.8.2:
version "23.6.0" version "23.8.2"
resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-23.6.0.tgz#508b32f80d44058c8c01257c0ee718cfbd521e9d" resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-23.8.2.tgz#6f28b41c67ef635f803ebd9e168f6b73858eb8d4"
integrity sha512-GH8AhcFXspOLqak7fqnddLXEJsrFyvgO8Bm60SexvKSn1+3rWYESnCiWUOCUcBTprNSDSE4CtAZdM4EyV6gPPw== integrity sha512-xwbnvOsotSV27MtAe7s8uGWOori0nUsrXh2f1EnpmXua8sDfY6VZhHAhHg2sqK7HBNycRQExF074XSZ7DvfoFg==
dependencies: dependencies:
"@typescript-eslint/experimental-utils" "^2.5.0" "@typescript-eslint/experimental-utils" "^2.5.0"
micromatch "^4.0.2"
eslint-plugin-jsx-a11y@6.2.3: eslint-plugin-jsx-a11y@6.2.3:
version "6.2.3" version "6.2.3"
@ -1306,15 +1349,15 @@ 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.3.0: eslint-plugin-react-hooks@2.5.0:
version "2.3.0" version "2.5.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-2.3.0.tgz#53e073961f1f5ccf8dd19558036c1fac8c29d99a" resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-2.5.0.tgz#c50ab7ca5945ce6d1cf8248d9e185c80b54171b6"
integrity sha512-gLKCa52G4ee7uXzdLiorca7JIQZPPXRAQDXV83J4bUEeUuc5pIEyZYAZ45Xnxe5IuupxEqHS+hUhSLIimK1EMw== integrity sha512-bzvdX47Jx847bgAYf0FPX3u1oxU+mKU8tqrpj4UX9A96SbAmj/HVEefEy6rJUog5u8QIlOPTKZcBpGn5kkKfAQ==
eslint-plugin-react@7.18.0: eslint-plugin-react@7.19.0:
version "7.18.0" version "7.19.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.18.0.tgz#2317831284d005b30aff8afb7c4e906f13fa8e7e" resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.19.0.tgz#6d08f9673628aa69c5559d33489e855d83551666"
integrity sha512-p+PGoGeV4SaZRDsXqdj9OWcOrOpZn8gXoGPcIQTzo2IDMbAKhNDnME9myZWqO3Ic4R3YmwAZ1lDjWl2R2hMUVQ== integrity sha512-SPT8j72CGuAP+JFbT0sJHOB80TX/pu44gQ4vXH/cq+hQTiY2PuZ6IHkqXJV6x1b28GDdo1lbInjKUrrdUf0LOQ==
dependencies: dependencies:
array-includes "^3.1.1" array-includes "^3.1.1"
doctrine "^2.1.0" doctrine "^2.1.0"
@ -1324,29 +1367,27 @@ eslint-plugin-react@7.18.0:
object.fromentries "^2.0.2" object.fromentries "^2.0.2"
object.values "^1.1.1" object.values "^1.1.1"
prop-types "^15.7.2" prop-types "^15.7.2"
resolve "^1.14.2" resolve "^1.15.1"
semver "^6.3.0"
string.prototype.matchall "^4.0.2"
xregexp "^4.3.0"
eslint-plugin-unicorn@15.0.1: eslint-plugin-unicorn@17.2.0:
version "15.0.1" version "17.2.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-unicorn/-/eslint-plugin-unicorn-15.0.1.tgz#8379d1d6882f9f8631bec8025ac9e8fe89e43945" resolved "https://registry.yarnpkg.com/eslint-plugin-unicorn/-/eslint-plugin-unicorn-17.2.0.tgz#8f147ba24d417dc5de948c7df7d006108a37a540"
integrity sha512-yahqrPGFUzwH5cnmnj+iPlgPapAiBIZ/ZNSDkhTVPgPCo7/mOEjJ2gDhEclrtQVBE9olmec4N+CKDnJuZ9XpRA== integrity sha512-0kYjrywf0kQxevFz571KrDfYMIRZ5Kq6dDgPU1EEBFeC181r+fAaPatBScWX+/hisKJ4+eCRFebxTeVylsSYmw==
dependencies: dependencies:
ci-info "^2.0.0" ci-info "^2.0.0"
clean-regexp "^1.0.0" clean-regexp "^1.0.0"
eslint-ast-utils "^1.1.0" eslint-ast-utils "^1.1.0"
eslint-template-visitor "^1.1.0" eslint-template-visitor "^1.1.0"
import-modules "^2.0.0" import-modules "^2.0.0"
lodash.camelcase "^4.3.0" lodash "^4.17.15"
lodash.defaultsdeep "^4.6.1"
lodash.kebabcase "^4.1.1"
lodash.snakecase "^4.1.1"
lodash.upperfirst "^4.3.1"
read-pkg-up "^7.0.1" read-pkg-up "^7.0.1"
regexp-tree "^0.1.17" regexp-tree "^0.1.20"
regexpp "^3.0.0"
reserved-words "^0.1.2" reserved-words "^0.1.2"
safe-regex "^2.1.1" safe-regex "^2.1.1"
semver "^6.3.0" semver "^7.1.2"
eslint-rule-composer@^0.3.0: eslint-rule-composer@^0.3.0:
version "0.3.0" version "0.3.0"
@ -2141,10 +2182,10 @@ 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.1: husky@^4.2.3:
version "4.2.1" version "4.2.3"
resolved "https://registry.yarnpkg.com/husky/-/husky-4.2.1.tgz#b09f1bd9129e6c323cc515dc17081d0615e2d7c1" resolved "https://registry.yarnpkg.com/husky/-/husky-4.2.3.tgz#3b18d2ee5febe99e27f2983500202daffbc3151e"
integrity sha512-Qa0lRreeIf4Tl92sSs42ER6qc3hzoyQPPorzOrFWfPEVbdi6LuvJEqWKPk905fOWIR76iBpp7ECZNIwk+a8xuQ== integrity sha512-VxTsSTRwYveKXN4SaH1/FefRJYCtx+wx04sSVcOpD7N2zjoHxa+cEJ07Qg5NmV3HAK+IRKOyNVpi2YBIVccIfQ==
dependencies: dependencies:
chalk "^3.0.0" chalk "^3.0.0"
ci-info "^2.0.0" ci-info "^2.0.0"
@ -2157,10 +2198,10 @@ husky@^4.2.1:
slash "^3.0.0" slash "^3.0.0"
which-pm-runs "^1.0.0" which-pm-runs "^1.0.0"
i18next@^19.0.3: i18next@^19.3.3:
version "19.1.0" version "19.3.3"
resolved "https://registry.yarnpkg.com/i18next/-/i18next-19.1.0.tgz#fe1a1da3d208872946307c7d2d115da45d46159f" resolved "https://registry.yarnpkg.com/i18next/-/i18next-19.3.3.tgz#04bd79b315e5fe2c87ab8f411e5d55eda0a17bd8"
integrity sha512-ISbmukX4L6Dz0QoH9+EW1AnBw7j+NRLoMu9uLPMaNSSTP9Eie9/oUL0dOyWX15baB3gYOpkHJpGZRHOqcnl0ew== integrity sha512-CnuPqep5/JsltkGvQqzYN4d79eCe0TreCBRF3a8qHHi8x4SON1qqZ/pvR2X7BfNkNqpA5HXIqw0E731H+VsgSg==
dependencies: dependencies:
"@babel/runtime" "^7.3.1" "@babel/runtime" "^7.3.1"
@ -2339,6 +2380,15 @@ inquirer@^7.0.0:
strip-ansi "^5.1.0" strip-ansi "^5.1.0"
through "^2.3.6" through "^2.3.6"
internal-slot@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.2.tgz#9c2e9fb3cd8e5e4256c6f45fe310067fcfa378a3"
integrity sha512-2cQNfwhAfJIkU4KZPkDI+Gj5yNNnbqi40W9Gge6dfnk4TocEVm00B3bdiL+JINrbGJil2TeHvM4rETGzk/f/0g==
dependencies:
es-abstract "^1.17.0-next.1"
has "^1.0.3"
side-channel "^1.0.2"
ipaddr.js@1.9.0: ipaddr.js@1.9.0:
version "1.9.0" version "1.9.0"
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65"
@ -2764,10 +2814,10 @@ linkify-it@^2.0.0:
dependencies: dependencies:
uc.micro "^1.0.1" uc.micro "^1.0.1"
lint-staged@^10.0.2: lint-staged@^10.0.8:
version "10.0.7" version "10.0.8"
resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.0.7.tgz#d205f92d9359419a23bc6aa3b6f8546b1998da64" resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.0.8.tgz#0f7849cdc336061f25f5d4fcbcfa385701ff4739"
integrity sha512-Byj0F4l7GYUpYYHEqyFH69NiI6ICTg0CeCKbhRorL+ickbzILKUlZLiyCkljZV02wnoh7yH7PmFyYm9PRNwk9g== integrity sha512-Oa9eS4DJqvQMVdywXfEor6F4vP+21fPHF8LUXgBbVWUSWBddjqsvO6Bv1LwMChmgQZZqwUvgJSHlu8HFHAPZmA==
dependencies: dependencies:
chalk "^3.0.0" chalk "^3.0.0"
commander "^4.0.1" commander "^4.0.1"
@ -2852,36 +2902,11 @@ locate-path@^5.0.0:
dependencies: dependencies:
p-locate "^4.1.0" p-locate "^4.1.0"
lodash.camelcase@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY=
lodash.defaultsdeep@^4.6.1:
version "4.6.1"
resolved "https://registry.yarnpkg.com/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz#512e9bd721d272d94e3d3a63653fa17516741ca6"
integrity sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==
lodash.get@^4.4.2: lodash.get@^4.4.2:
version "4.4.2" version "4.4.2"
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=
lodash.kebabcase@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36"
integrity sha1-hImxyw0p/4gZXM7KRI/21swpXDY=
lodash.snakecase@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d"
integrity sha1-OdcUo1NXFHg3rv1ktdy7Fr7Nj40=
lodash.upperfirst@^4.3.1:
version "4.3.1"
resolved "https://registry.yarnpkg.com/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz#1365edf431480481ef0d1c68957a5ed99d49f7ce"
integrity sha1-E2Xt9DFIBIHvDRxolXpe2Z1J984=
lodash.zip@^4.2.0: lodash.zip@^4.2.0:
version "4.2.0" version "4.2.0"
resolved "https://registry.yarnpkg.com/lodash.zip/-/lodash.zip-4.2.0.tgz#ec6662e4896408ed4ab6c542a3990b72cc080020" resolved "https://registry.yarnpkg.com/lodash.zip/-/lodash.zip-4.2.0.tgz#ec6662e4896408ed4ab6c542a3990b72cc080020"
@ -3782,6 +3807,11 @@ regenerator-runtime@^0.13.2:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5"
integrity sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw== integrity sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==
regenerator-runtime@^0.13.4:
version "0.13.5"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697"
integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==
regex-cache@^0.4.2: regex-cache@^0.4.2:
version "0.4.4" version "0.4.4"
resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd"
@ -3797,11 +3827,24 @@ regex-not@^1.0.0, regex-not@^1.0.2:
extend-shallow "^3.0.2" extend-shallow "^3.0.2"
safe-regex "^1.1.0" safe-regex "^1.1.0"
regexp-tree@^0.1.17, regexp-tree@~0.1.1: regexp-tree@^0.1.20:
version "0.1.21"
resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.21.tgz#55e2246b7f7d36f1b461490942fa780299c400d7"
integrity sha512-kUUXjX4AnqnR8KRTCrayAo9PzYMRKmVoGgaz2tBuz0MF3g1ZbGebmtW0yFHfFK9CmBjQKeYIgoL22pFLBJY7sw==
regexp-tree@~0.1.1:
version "0.1.18" version "0.1.18"
resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.18.tgz#ed4819a9f03ec2de9613421d6eaf47512e7fdaf1" resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.18.tgz#ed4819a9f03ec2de9613421d6eaf47512e7fdaf1"
integrity sha512-mKLUfTDU1GE5jGR7cn2IEPDzYjmOviZOHYAR1XGe8Lg48Mdk684waD1Fqhv2Nef+TsDVdmIj08m/GUKTMk7J2Q== integrity sha512-mKLUfTDU1GE5jGR7cn2IEPDzYjmOviZOHYAR1XGe8Lg48Mdk684waD1Fqhv2Nef+TsDVdmIj08m/GUKTMk7J2Q==
regexp.prototype.flags@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75"
integrity sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==
dependencies:
define-properties "^1.1.3"
es-abstract "^1.17.0-next.1"
regexpp@^2.0.1: regexpp@^2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f"
@ -3897,13 +3940,20 @@ resolve-url@^0.2.1:
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
resolve@^1.10.0, resolve@^1.10.1, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2: resolve@^1.10.0, resolve@^1.10.1, resolve@^1.12.0, resolve@^1.13.1:
version "1.15.0" version "1.15.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.0.tgz#1b7ca96073ebb52e741ffd799f6b39ea462c67f5" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.0.tgz#1b7ca96073ebb52e741ffd799f6b39ea462c67f5"
integrity sha512-+hTmAldEGE80U2wJJDC1lebb5jWqvTYAfm3YZ1ckk1gBr0MnCqUKlwK1e+anaFljIl+F5tR5IoZcm4ZDA1zMQw== integrity sha512-+hTmAldEGE80U2wJJDC1lebb5jWqvTYAfm3YZ1ckk1gBr0MnCqUKlwK1e+anaFljIl+F5tR5IoZcm4ZDA1zMQw==
dependencies: dependencies:
path-parse "^1.0.6" path-parse "^1.0.6"
resolve@^1.15.1:
version "1.15.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8"
integrity sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==
dependencies:
path-parse "^1.0.6"
restore-cursor@^2.0.0: restore-cursor@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
@ -4007,6 +4057,11 @@ semver@^6.1.0, semver@^6.1.2, semver@^6.3.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
semver@^7.1.2:
version "7.1.3"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.1.3.tgz#e4345ce73071c53f336445cfc19efb1c311df2a6"
integrity sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==
send@0.17.1: send@0.17.1:
version "0.17.1" version "0.17.1"
resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8"
@ -4080,6 +4135,14 @@ shorthash@0.0.2:
resolved "https://registry.yarnpkg.com/shorthash/-/shorthash-0.0.2.tgz#59b268eecbde59038b30da202bcfbddeb2c4a4eb" resolved "https://registry.yarnpkg.com/shorthash/-/shorthash-0.0.2.tgz#59b268eecbde59038b30da202bcfbddeb2c4a4eb"
integrity sha1-WbJo7sveWQOLMNogK8+93rLEpOs= integrity sha1-WbJo7sveWQOLMNogK8+93rLEpOs=
side-channel@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.2.tgz#df5d1abadb4e4bf4af1cd8852bf132d2f7876947"
integrity sha512-7rL9YlPHg7Ancea1S96Pa8/QWb4BtXL/TZvS6B8XFetGBeuhAsfmUspK6DokBeZ64+Kj9TCNRD/30pVz1BvQNA==
dependencies:
es-abstract "^1.17.0-next.1"
object-inspect "^1.7.0"
signal-exit@^3.0.2: signal-exit@^3.0.2:
version "3.0.2" version "3.0.2"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
@ -4134,10 +4197,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.0.1: sortpack@^2.1.2:
version "2.1.1" version "2.1.2"
resolved "https://registry.yarnpkg.com/sortpack/-/sortpack-2.1.1.tgz#e94280616a517851257728721dd6749619aca309" resolved "https://registry.yarnpkg.com/sortpack/-/sortpack-2.1.2.tgz#25bf86f2923c81f43a00a2166ff4d271fafeed11"
integrity sha512-/jtQAzl9JeTXZxzznW6L729M+Q7uv9k9Dm89eF0UxMj4Rna3CmO0IYT0MUS6aLyHUOTnwpT7kIDs4PQmMTEhLw== integrity sha512-43fSND1vmAdyfgC38aOkVxZBV331f4blF8acjwQmx7Gba4nuL2ene/Cq5eixNmDhKA/qQHnvSeAl+jEWb31rfg==
source-map-resolve@^0.5.0: source-map-resolve@^0.5.0:
version "0.5.3" version "0.5.3"
@ -4299,6 +4362,18 @@ string-width@^4.1.0:
is-fullwidth-code-point "^3.0.0" is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.0" strip-ansi "^6.0.0"
string.prototype.matchall@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.2.tgz#48bb510326fb9fdeb6a33ceaa81a6ea04ef7648e"
integrity sha512-N/jp6O5fMf9os0JU3E72Qhf590RSRZU/ungsL/qJUYVTNv7hTG0P/dbPjxINVN9jpscu3nzYwKESU3P3RY5tOg==
dependencies:
define-properties "^1.1.3"
es-abstract "^1.17.0"
has-symbols "^1.0.1"
internal-slot "^1.0.2"
regexp.prototype.flags "^1.3.0"
side-channel "^1.0.2"
string.prototype.trimleft@^2.1.1: string.prototype.trimleft@^2.1.1:
version "2.1.1" version "2.1.1"
resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz#9bdb8ac6abd6d602b17a4ed321870d2f8dcefc74" resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz#9bdb8ac6abd6d602b17a4ed321870d2f8dcefc74"
@ -4408,10 +4483,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.3: terser@^4.6.7:
version "4.6.3" version "4.6.7"
resolved "https://registry.yarnpkg.com/terser/-/terser-4.6.3.tgz#e33aa42461ced5238d352d2df2a67f21921f8d87" resolved "https://registry.yarnpkg.com/terser/-/terser-4.6.7.tgz#478d7f9394ec1907f0e488c5f6a6a9a2bad55e72"
integrity sha512-Lw+ieAXmY69d09IIc/yqeBqXpEQIpDGZqT34ui1QWXIUpR2RjbqEkT8X7Lgex19hslSqcWM5iMN2kM11eMsESQ== integrity sha512-fmr7M1f7DBly5cX2+rFDvmGBAaaZyPrHYK4mMdHEDAdNTqXSZgSOfqsfGq2HqPGT/1V0foZZuCZFx8CHKgAk3g==
dependencies: dependencies:
commander "^2.20.0" commander "^2.20.0"
source-map "~0.6.1" source-map "~0.6.1"
@ -4437,12 +4512,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.0.0: tippy.js@^6.1.0:
version "6.0.0" version "6.1.0"
resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-6.0.0.tgz#6bc4f477ea82ef344db51ae34f106a8b25f2d02c" resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-6.1.0.tgz#9c58b94f92f3044d5e861b9d83da3c2a6d3d4323"
integrity sha512-2NVc5A8cnO9N/Fk+tTU6KEm6m8ZXT1u3pOY756zZ5BE38rWDl/hVyoWhTfM79HW1nEJSpn/VujqAMMZGHsE9qQ== integrity sha512-cRFydlVZlvo4soQSUfVNbH2K77zDUhDAzaAjxseyn81gGIa+j72y98yDL2yB0n8gas/E+Zlr1iOyR5ckslUFqA==
dependencies: dependencies:
"@popperjs/core" "^2.0.6" "@popperjs/core" "^2.1.1"
tmp@^0.0.33: tmp@^0.0.33:
version "0.0.33" version "0.0.33"
@ -4488,10 +4563,10 @@ to-regex@^3.0.1, to-regex@^3.0.2:
regex-not "^1.0.2" regex-not "^1.0.2"
safe-regex "^1.1.0" safe-regex "^1.1.0"
toastify-js@^1.6.2: toastify-js@^1.7.0:
version "1.6.2" version "1.7.0"
resolved "https://registry.yarnpkg.com/toastify-js/-/toastify-js-1.6.2.tgz#38af35625797d3d3f51fa09851f0bda449271423" resolved "https://registry.yarnpkg.com/toastify-js/-/toastify-js-1.7.0.tgz#d6b44937ae2844a19c25fcc69ee5933165dbf666"
integrity sha512-ECQzgjTjxaElfwp/8e8qoIYx7U5rU2G54e5aiPMv+UtmGOYEitrtNp/Kr8uMgntnQNrDZEQJNGjBtoNnEgR5EA== integrity sha512-GmPy4zJ/ulCfmCHlfCtgcB+K2xhx2AXW3T/ZZOSjyjaIGevhz+uvR8HSCTay/wBq4tt2mUnBqlObP1sSWGlsnQ==
toidentifier@1.0.0: toidentifier@1.0.0:
version "1.0.0" version "1.0.0"
@ -4506,15 +4581,15 @@ tough-cookie@~2.4.3:
psl "^1.1.24" psl "^1.1.24"
punycode "^1.4.1" punycode "^1.4.1"
tributejs@^5.0.0: tributejs@^5.1.2:
version "5.0.0" version "5.1.2"
resolved "https://registry.yarnpkg.com/tributejs/-/tributejs-5.0.0.tgz#2c5301a79c19d7a72d23e995bf7c9f47c2a34f23" resolved "https://registry.yarnpkg.com/tributejs/-/tributejs-5.1.2.tgz#d8492d974d3098d6016248d689fb063cda6e77f7"
integrity sha512-aPUpq4+NTXRq1OcdoeiFg9d+wM+J0b7dpL7MNVxqo8JIgChtkx8HnsPVl/uZ4Z1ChTF9UI1ffbvTfJRHqJjjAw== integrity sha512-R9ff/q6w4T5f3Y9+RL+qinog3X1eAj1UnR/yfZaGJ8D3wuJs4/vicrGYul9+fgS9EJ/iYgwARekTb92xwark0g==
ts-node@^8.6.2: ts-node@^8.7.0:
version "8.6.2" version "8.7.0"
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.6.2.tgz#7419a01391a818fbafa6f826a33c1a13e9464e35" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.7.0.tgz#266186947596bef9f3a034687595b30e31b20976"
integrity sha512-4mZEbofxGqLL2RImpe3zMJukvEvcO1XP8bj8ozBPySdCUXEcU5cIRwR0aM3R+VoZq7iXc8N86NC0FspGRqP4gg== integrity sha512-s659CsHrsxaRVDEleuOkGvbsA0rWHtszUNEt1r0CgAFN5ZZTQtDzpsluS7W5pOGJIa1xZE8R/zK4dEs+ldFezg==
dependencies: dependencies:
arg "^4.1.0" arg "^4.1.0"
diff "^4.0.1" diff "^4.0.1"
@ -4803,10 +4878,17 @@ ws@^1.1.1:
options ">=0.0.5" options ">=0.0.5"
ultron "1.0.x" ultron "1.0.x"
ws@^7.0.0: ws@^7.2.3:
version "7.2.1" version "7.2.3"
resolved "https://registry.yarnpkg.com/ws/-/ws-7.2.1.tgz#03ed52423cd744084b2cf42ed197c8b65a936b8e" resolved "https://registry.yarnpkg.com/ws/-/ws-7.2.3.tgz#a5411e1fb04d5ed0efee76d26d5c46d830c39b46"
integrity sha512-sucePNSafamSKoOqoNfBd8V0StlkzJKL2ZAhGQinCfNQ+oacw+Pk7lcdAElecBF2VkLNZRiIb5Oi1Q5lVUVt2A== integrity sha512-HTDl9G9hbkNDk98naoR/cHDws7+EyYMOdL1BmjsZXRUjf7d+MficC4B7HLUPlSiho0vg+CWKrGIt/VJBd1xunQ==
xregexp@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.3.0.tgz#7e92e73d9174a99a59743f67a4ce879a04b5ae50"
integrity sha512-7jXDIFXh5yJ/orPn4SXjuVrWWoi4Cr8jfV1eHv9CixKSbU+jY4mxfrBwAuDvupPNKpMUY+FeIqsVw/JLT9+B8g==
dependencies:
"@babel/runtime-corejs3" "^7.8.3"
yaml@^1.7.2: yaml@^1.7.2:
version "1.7.2" version "1.7.2"