commit
1e8b359571
80 changed files with 3108 additions and 1324 deletions
31
README.md
vendored
31
README.md
vendored
|
@ -1,5 +1,5 @@
|
|||
<p align="center">
|
||||
<a href="" rel="noopener">
|
||||
<a href="https://dev.lemmy.ml/" rel="noopener">
|
||||
<img width=200px height=200px src="ui/assets/favicon.svg"></a>
|
||||
</p>
|
||||
|
||||
|
@ -113,7 +113,7 @@ docker-compose up -d
|
|||
|
||||
and go to http://localhost:8536.
|
||||
|
||||
[A sample nginx config](/ansible/templates/nginx.conf), could be setup with:
|
||||
[A sample nginx config](/ansible/templates/nginx.conf) (Image uploading won't work without this), could be setup with:
|
||||
|
||||
```bash
|
||||
wget https://raw.githubusercontent.com/dessalines/lemmy/master/ansible/templates/nginx.conf
|
||||
|
@ -159,25 +159,26 @@ Lemmy is free, open-source software, meaning no advertising, monetizing, or vent
|
|||
|
||||
## Translations
|
||||
|
||||
If you'd like to add translations, take a look a look at the [English translation file](ui/src/translations/en.ts).
|
||||
If you'd like to add translations, take a look at the [English translation file](ui/src/translations/en.ts).
|
||||
|
||||
- Languages supported: Catalan, (`ca`), English (`en`), Chinese (`zh`), Dutch (`nl`), Esperanto (`eo`), Finnish (`fi`), French (`fr`), Spanish (`es`), Swedish (`sv`), German (`de`), Russian (`ru`), Italian (`it`).
|
||||
- Languages supported: Catalan, (`ca`), Farsi (`fa`), English (`en`), Chinese (`zh`), Dutch (`nl`), Esperanto (`eo`), Finnish (`fi`), French (`fr`), Spanish (`es`), Swedish (`sv`), German (`de`), Russian (`ru`), Italian (`it`).
|
||||
|
||||
<!-- translations -->
|
||||
|
||||
lang | done | missing
|
||||
---- | ---- | -------
|
||||
ca | 100% | old
|
||||
de | 87% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,old,docs,message_sent,messages,old_password,matrix_user_id,private_message_disclaimer,send_notifications_to_email,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
|
||||
eo | 75% | number_of_communities,create_private_message,send_secure_message,send_message,message,preview,upload_image,avatar,upload_avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,old,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,theme,donate_to_lemmy,donate,from,are_you_sure,yes,no,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
|
||||
es | 100% | old
|
||||
fi | 100% | old
|
||||
fr | 83% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,archive_link,old,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
|
||||
it | 84% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,archive_link,old,docs,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
|
||||
nl | 92% | create_private_message,send_secure_message,send_message,message,old,message_sent,messages,matrix_user_id,private_message_disclaimer,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
|
||||
ru | 72% | cross_posts,cross_post,number_of_communities,create_private_message,send_secure_message,send_message,message,preview,upload_image,avatar,upload_avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,old,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,theme,donate_to_lemmy,donate,monero,by,to,from,transfer_community,transfer_site,are_you_sure,yes,no,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
|
||||
sv | 83% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,archive_link,old,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
|
||||
zh | 70% | cross_posts,cross_post,users,number_of_communities,create_private_message,send_secure_message,send_message,message,preview,upload_image,avatar,upload_avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,settings,stickied,delete_account,delete_account_confirm,banned,creator,number_online,old,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,nsfw,show_nsfw,theme,donate_to_lemmy,donate,monero,by,to,from,transfer_community,transfer_site,are_you_sure,yes,no,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
|
||||
ca | 99% | old,time,action
|
||||
de | 87% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,old,docs,message_sent,messages,old_password,matrix_user_id,private_message_disclaimer,send_notifications_to_email,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message,time,action
|
||||
fa | 72% | cross_post,subscribed_to_communities,trending_communities,create_private_message,send_secure_message,send_message,message,mod,mods,moderates,remove_as_mod,appoint_as_mod,modlog,stickied,ban,ban_from_site,unban,unban_from_site,banned,number_of_subscribers,subscribers,both,saved,unsubscribe,subscribe,subscribed,old,api,docs,inbox,inbox_for,message_sent,notifications_error,messages,no_email_setup,matrix_user_id,private_message_disclaimer,url,body,copy_suggested_title,community,expand_here,subscribe_to_communities,theme,sponsor_message,general_sponsors,joined,by,to,from,landing_0,logged_in,community_moderator_already_exists,community_follower_already_exists,community_user_already_banned,no_slurs,admin_already_created,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message,time,action
|
||||
eo | 75% | number_of_communities,create_private_message,send_secure_message,send_message,message,preview,upload_image,avatar,upload_avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,old,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,theme,donate_to_lemmy,donate,from,are_you_sure,yes,no,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message,time,action
|
||||
es | 99% | old,time,action
|
||||
fi | 99% | old,time,action
|
||||
fr | 82% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,archive_link,old,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message,time,action
|
||||
it | 83% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,archive_link,old,docs,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message,time,action
|
||||
nl | 99% | time,action
|
||||
ru | 71% | cross_posts,cross_post,number_of_communities,create_private_message,send_secure_message,send_message,message,preview,upload_image,avatar,upload_avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,old,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,theme,donate_to_lemmy,donate,monero,by,to,from,transfer_community,transfer_site,are_you_sure,yes,no,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message,time,action
|
||||
sv | 82% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,archive_link,old,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message,time,action
|
||||
zh | 69% | cross_posts,cross_post,users,number_of_communities,create_private_message,send_secure_message,send_message,message,preview,upload_image,avatar,upload_avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,settings,stickied,delete_account,delete_account_confirm,banned,creator,number_online,old,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,nsfw,show_nsfw,theme,donate_to_lemmy,donate,monero,by,to,from,transfer_community,transfer_site,are_you_sure,yes,no,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message,time,action
|
||||
<!-- translationsstop -->
|
||||
|
||||
If you'd like to update this report, run:
|
||||
|
|
1
ansible/VERSION
vendored
Normal file
1
ansible/VERSION
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
v0.6.10
|
2
ansible/inventory.example
vendored
2
ansible/inventory.example
vendored
|
@ -1,6 +1,6 @@
|
|||
[lemmy]
|
||||
# define the username and hostname that you use for ssh connection, and specify the domain
|
||||
myuser@example.com domain=example.com letsencrypt_contact_email=your@email.com smtp_server=smtp@example.com smtp_login=your@email.com smtp_password=pass smtp_from_address="Example.com Admin <notifications@example.com>"
|
||||
myuser@example.com domain=example.com letsencrypt_contact_email=your@email.com
|
||||
|
||||
[all:vars]
|
||||
ansible_connection=ssh
|
||||
|
|
14
ansible/lemmy.yml
vendored
14
ansible/lemmy.yml
vendored
|
@ -29,15 +29,19 @@
|
|||
- { path: '/lemmy/' }
|
||||
- { path: '/lemmy/volumes/' }
|
||||
|
||||
- name: add all template files
|
||||
template: src={{item.src}} dest={{item.dest}}
|
||||
- block:
|
||||
- name: add template files
|
||||
template: src={{item.src}} dest={{item.dest}} mode={{item.mode}}
|
||||
with_items:
|
||||
- { src: '../docker/prod/docker-compose.yml', dest: '/lemmy/docker-compose.yml' }
|
||||
- { src: 'templates/config.hjson', dest: '/lemmy/lemmy.hjson' }
|
||||
- { src: 'templates/nginx.conf', dest: '/etc/nginx/sites-enabled/lemmy.conf' }
|
||||
- { 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' }
|
||||
|
||||
- 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'
|
||||
vars:
|
||||
postgres_password: "{{ lookup('password', 'passwords/{{ inventory_hostname }}/postgres 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
|
||||
systemd:
|
||||
|
|
100
ansible/lemmy_dev.yml
vendored
Normal file
100
ansible/lemmy_dev.yml
vendored
Normal file
|
@ -0,0 +1,100 @@
|
|||
---
|
||||
- hosts: all
|
||||
vars:
|
||||
lemmy_docker_image: "lemmy:dev"
|
||||
|
||||
# Install python if required
|
||||
# https://www.josharcher.uk/code/ansible-python-connection-failure-ubuntu-server-1604/
|
||||
gather_facts: False
|
||||
pre_tasks:
|
||||
- name: install python for Ansible
|
||||
raw: test -e /usr/bin/python || (apt -y update && apt install -y python-minimal python-setuptools)
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: output
|
||||
changed_when: output.stdout != ""
|
||||
- setup: # gather facts
|
||||
|
||||
tasks:
|
||||
- name: install dependencies
|
||||
apt:
|
||||
pkg: ['nginx', 'docker-compose', 'docker.io', 'certbot', 'python-certbot-nginx']
|
||||
|
||||
- name: request initial letsencrypt certificate
|
||||
command: certbot certonly --nginx --agree-tos -d '{{ domain }}' -m '{{ letsencrypt_contact_email }}'
|
||||
args:
|
||||
creates: '/etc/letsencrypt/live/{{domain}}/privkey.pem'
|
||||
|
||||
- name: create lemmy folder
|
||||
file: path={{item.path}} state=directory
|
||||
with_items:
|
||||
- { path: '/lemmy/' }
|
||||
- { path: '/lemmy/volumes/' }
|
||||
|
||||
- block:
|
||||
- name: add template files
|
||||
template: src={{item.src}} dest={{item.dest}} mode={{item.mode}}
|
||||
with_items:
|
||||
- { src: 'templates/docker-compose.yml', dest: '/lemmy/docker-compose.yml', mode: '0600' }
|
||||
- { src: 'templates/nginx.conf', dest: '/etc/nginx/sites-enabled/lemmy.conf', mode: '0644' }
|
||||
|
||||
- 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'
|
||||
vars:
|
||||
postgres_password: "{{ lookup('password', 'passwords/{{ inventory_hostname }}/postgres chars=ascii_letters,digits') }}"
|
||||
jwt_password: "{{ lookup('password', 'passwords/{{ inventory_hostname }}/jwt chars=ascii_letters,digits') }}"
|
||||
|
||||
- name: build the dev docker image
|
||||
local_action: shell cd .. && sudo docker build . -f docker/dev/Dockerfile -t lemmy:dev
|
||||
register: image_build
|
||||
|
||||
- name: find hash of the new docker image
|
||||
set_fact:
|
||||
image_hash: "{{ image_build.stdout | regex_search('(?<=Successfully built )[0-9a-f]{12}') }}"
|
||||
|
||||
# this does not use become so that the output file is written as non-root user and is easy to delete later
|
||||
- name: save dev docker image to file
|
||||
local_action: shell sudo docker save lemmy:dev > lemmy-dev.tar
|
||||
|
||||
- name: copy dev docker image to server
|
||||
copy: src=lemmy-dev.tar dest=/lemmy/lemmy-dev.tar
|
||||
|
||||
- name: import docker image
|
||||
docker_image:
|
||||
name: lemmy
|
||||
tag: dev
|
||||
load_path: /lemmy/lemmy-dev.tar
|
||||
source: load
|
||||
force_source: yes
|
||||
register: image_import
|
||||
|
||||
- name: delete remote image file
|
||||
file: path=/lemmy/lemmy-dev.tar state=absent
|
||||
|
||||
- name: delete local image file
|
||||
local_action: file path=lemmy-dev.tar state=absent
|
||||
|
||||
- name: enable and start docker service
|
||||
systemd:
|
||||
name: docker
|
||||
enabled: yes
|
||||
state: started
|
||||
|
||||
# cant pull here because that fails due to lemmy:dev (without dessalines/) not being on docker hub, but that shouldnt
|
||||
# be a problem for testing
|
||||
- name: start docker-compose
|
||||
docker_compose:
|
||||
project_src: /lemmy/
|
||||
state: present
|
||||
recreate: always
|
||||
ignore_errors: yes
|
||||
|
||||
- name: reload nginx with new config
|
||||
shell: nginx -s reload
|
||||
|
||||
- name: certbot renewal cronjob
|
||||
cron:
|
||||
special_time=daily
|
||||
name=certbot-renew-lemmy
|
||||
user=root
|
||||
job="certbot certonly --nginx -d '{{ domain }}' --deploy-hook 'docker-compose -f /peertube/docker-compose.yml exec nginx nginx -s reload'"
|
7
ansible/templates/config.hjson
vendored
7
ansible/templates/config.hjson
vendored
|
@ -7,9 +7,8 @@
|
|||
jwt_secret: "{{ jwt_password }}"
|
||||
front_end_dir: "/app/dist"
|
||||
email: {
|
||||
smtp_server: "{{ smtp_server }}"
|
||||
smtp_login: "{{ smtp_login }}"
|
||||
smtp_password: "{{ smtp_password }}"
|
||||
smtp_from_address: "{{ smtp_from_address }}"
|
||||
smtp_server: "postfix:25"
|
||||
smtp_from_address: "noreply@{{ domain }}"
|
||||
use_tls: false
|
||||
}
|
||||
}
|
||||
|
|
40
ansible/templates/docker-compose.yml
vendored
Normal file
40
ansible/templates/docker-compose.yml
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
version: '3.3'
|
||||
|
||||
services:
|
||||
lemmy:
|
||||
image: {{ lemmy_docker_image }}
|
||||
ports:
|
||||
- "127.0.0.1:8536:8536"
|
||||
restart: always
|
||||
volumes:
|
||||
- ./lemmy.hjson:/config/config.hjson:ro
|
||||
depends_on:
|
||||
- lemmy_db
|
||||
- lemmy_pictshare
|
||||
|
||||
lemmy_db:
|
||||
image: postgres:12-alpine
|
||||
environment:
|
||||
- POSTGRES_USER=lemmy
|
||||
- POSTGRES_PASSWORD={{ postgres_password }}
|
||||
- POSTGRES_DB=lemmy
|
||||
volumes:
|
||||
- lemmy_db:/var/lib/postgresql/data
|
||||
restart: always
|
||||
|
||||
lemmy_pictshare:
|
||||
image: shtripok/pictshare:latest
|
||||
ports:
|
||||
- "127.0.0.1:8537:80"
|
||||
volumes:
|
||||
- lemmy_pictshare:/usr/share/nginx/html/data
|
||||
restart: always
|
||||
|
||||
postfix:
|
||||
image: mwader/postfix-relay
|
||||
environment:
|
||||
- POSTFIX_myhostname={{ domain }}
|
||||
restart: "always"
|
||||
volumes:
|
||||
lemmy_db:
|
||||
lemmy_pictshare:
|
2
ansible/templates/nginx.conf
vendored
2
ansible/templates/nginx.conf
vendored
|
@ -93,4 +93,4 @@ map $remote_addr $remote_addr_anon {
|
|||
}
|
||||
log_format main '$remote_addr_anon - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" "$http_user_agent"';
|
||||
access_log /dev/stdout main;
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
|
48
ansible/uninstall.yml
vendored
Normal file
48
ansible/uninstall.yml
vendored
Normal file
|
@ -0,0 +1,48 @@
|
|||
---
|
||||
- hosts: all
|
||||
|
||||
vars_prompt:
|
||||
|
||||
- name: confirm_uninstall
|
||||
prompt: "Do you really want to uninstall Lemmy? This will delete all data and can not be reverted [yes/no]"
|
||||
private: no
|
||||
|
||||
- name: delete_certs
|
||||
prompt: "Delete certificates? Select 'no' if you want to reinstall Lemmy [yes/no]"
|
||||
private: no
|
||||
|
||||
tasks:
|
||||
- name: end play if no confirmation was given
|
||||
debug:
|
||||
msg: "Uninstall cancelled, doing nothing"
|
||||
when: not confirm_uninstall|bool
|
||||
|
||||
- meta: end_play
|
||||
when: not confirm_uninstall|bool
|
||||
|
||||
- name: stop docker-compose
|
||||
docker_compose:
|
||||
project_src: /lemmy/
|
||||
state: absent
|
||||
|
||||
- name: delete data
|
||||
file: path={{item.path}} state=absent
|
||||
with_items:
|
||||
- { path: '/lemmy/' }
|
||||
- { path: '/etc/nginx/sites-enabled/lemmy.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
|
||||
cron:
|
||||
name=certbot-renew-lemmy
|
||||
state=absent
|
||||
|
31
docker/dev/Dockerfile.libc
vendored
31
docker/dev/Dockerfile.libc
vendored
|
@ -20,10 +20,11 @@ COPY ui /app/ui
|
|||
RUN yarn build
|
||||
|
||||
|
||||
FROM rust:1.37 as rust
|
||||
FROM rust:1.40 as rust
|
||||
|
||||
# Cache deps
|
||||
WORKDIR /app
|
||||
|
||||
RUN USER=root cargo new server
|
||||
WORKDIR /app/server
|
||||
COPY server/Cargo.toml server/Cargo.lock ./
|
||||
|
@ -31,24 +32,35 @@ RUN mkdir -p ./src/bin \
|
|||
&& echo 'fn main() { println!("Dummy") }' > ./src/bin/main.rs
|
||||
|
||||
|
||||
#RUN cargo build --release
|
||||
RUN cargo build && \
|
||||
rm -f ./target/release/deps/lemmy_server* ; rm -f ./target/debug/deps/lemmy_server*
|
||||
RUN cargo build --release
|
||||
#RUN cargo build && \
|
||||
# rm -f ./target/release/deps/lemmy_server* ; rm -f ./target/debug/deps/lemmy_server*
|
||||
COPY server/src ./src/
|
||||
COPY server/migrations ./migrations/
|
||||
|
||||
|
||||
# build for release
|
||||
# workaround for https://github.com/rust-lang/rust/issues/62896
|
||||
#RUN RUSTFLAGS='-Ccodegen-units=1' cargo build --release
|
||||
#RUN cargo build --release --frozen
|
||||
RUN cargo build --frozen
|
||||
RUN cargo build --release --frozen
|
||||
#RUN cargo build --frozen
|
||||
|
||||
# Get diesel-cli on there just in case
|
||||
# RUN cargo install diesel_cli --no-default-features --features postgres
|
||||
|
||||
# make result place always the same for lemmy container
|
||||
#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/release/lemmy_server /app/server/ready
|
||||
#RUN cp /app/server/target/debug/lemmy_server /app/server/ready
|
||||
|
||||
|
||||
FROM rust:1.40 as docs
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Build docs
|
||||
COPY docs ./docs
|
||||
RUN cargo install mdbook
|
||||
RUN mdbook build docs/
|
||||
|
||||
|
||||
#FROM alpine:3.10
|
||||
|
@ -66,8 +78,9 @@ RUN adduser --disabled-password --shell /bin/sh --uid 1000 --ingroup lemmy lemmy
|
|||
|
||||
# Copy resources
|
||||
COPY server/config/defaults.hjson /config/defaults.hjson
|
||||
COPY --from=rust /app/server/ready /app/lemmy
|
||||
COPY --from=node /app/ui/dist /app/dist
|
||||
COPY --from=docs /app/docs/book/ /app/dist/documentation/
|
||||
COPY --from=rust /app/server/ready /app/lemmy
|
||||
|
||||
RUN chown lemmy:lemmy /app/lemmy
|
||||
USER lemmy
|
||||
|
|
5
docker/dev/deploy.sh
vendored
5
docker/dev/deploy.sh
vendored
|
@ -14,12 +14,17 @@ git add "ui/src/version.ts"
|
|||
# Setting the version on the backend
|
||||
echo "pub const VERSION: &str = \"$(git describe --tags)\";" > "server/src/version.rs"
|
||||
git add "server/src/version.rs"
|
||||
# Setting the version for Ansible
|
||||
git describe --tags > "ansible/VERSION"
|
||||
git add "ansible/VERSION"
|
||||
|
||||
cd docker/dev
|
||||
|
||||
# Changing the docker-compose prod
|
||||
sed -i "s/dessalines\/lemmy:.*/dessalines\/lemmy:$new_tag/" ../prod/docker-compose.yml
|
||||
sed -i "s/dessalines\/lemmy:.*/dessalines\/lemmy:$new_tag/" ../../ansible/templates/docker-compose.yml
|
||||
git add ../prod/docker-compose.yml
|
||||
git add ../../ansible/templates/docker-compose.yml
|
||||
|
||||
# The commit
|
||||
git commit -m"Version $new_tag"
|
||||
|
|
2
docker/prod/docker-compose.yml
vendored
2
docker/prod/docker-compose.yml
vendored
|
@ -11,7 +11,7 @@ services:
|
|||
- lemmy_db:/var/lib/postgresql/data
|
||||
restart: always
|
||||
lemmy:
|
||||
image: dessalines/lemmy:v0.6.7
|
||||
image: dessalines/lemmy:v0.6.10
|
||||
ports:
|
||||
- "127.0.0.1:8536:8536"
|
||||
restart: always
|
||||
|
|
409
server/Cargo.lock
generated
vendored
409
server/Cargo.lock
generated
vendored
|
@ -10,7 +10,7 @@ dependencies = [
|
|||
"activitystreams-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -29,7 +29,7 @@ version = "0.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thiserror 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -44,7 +44,7 @@ dependencies = [
|
|||
"mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -158,10 +158,10 @@ dependencies = [
|
|||
"mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pin-project 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -185,7 +185,7 @@ dependencies = [
|
|||
"bytestring 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -315,9 +315,9 @@ dependencies = [
|
|||
"mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pin-project 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -378,6 +378,11 @@ dependencies = [
|
|||
"memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
version = "0.4.4"
|
||||
|
@ -420,6 +425,11 @@ name = "autocfg"
|
|||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "awc"
|
||||
version = "1.0.1"
|
||||
|
@ -436,9 +446,9 @@ dependencies = [
|
|||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -493,7 +503,7 @@ dependencies = [
|
|||
"blowfish 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -561,6 +571,11 @@ name = "bufstream"
|
|||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "byte-tools"
|
||||
version = "0.3.1"
|
||||
|
@ -607,8 +622,8 @@ name = "chrono"
|
|||
version = "0.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
@ -631,7 +646,7 @@ dependencies = [
|
|||
"rust-ini 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde-hjson 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
@ -683,68 +698,57 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.9.0"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"darling_core 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"darling_macro 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"darling_core 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"darling_macro 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.9.0"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ident_case 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"strsim 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.9.0"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"darling_core 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"darling_core 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder"
|
||||
version = "0.7.2"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"darling 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"derive_builder_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"darling 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"derive_builder_core 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder_core"
|
||||
version = "0.5.0"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"darling 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"darling 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -926,7 +930,7 @@ dependencies = [
|
|||
"atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -1254,17 +1258,24 @@ version = "0.4.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "jsonwebtoken"
|
||||
version = "6.0.1"
|
||||
name = "js-sys"
|
||||
version = "0.3.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasm-bindgen 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jsonwebtoken"
|
||||
version = "7.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pem 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ring 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"simple_asn1 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1311,16 +1322,16 @@ dependencies = [
|
|||
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hjson 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonwebtoken 6.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonwebtoken 7.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lettre 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lettre_email 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rss 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rss 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"strum 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"strum_macros 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
@ -1339,7 +1350,7 @@ dependencies = [
|
|||
"nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1560,12 +1571,22 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.41"
|
||||
name = "num-bigint"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1573,15 +1594,15 @@ name = "num-traits"
|
|||
version = "0.1.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.10"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1650,6 +1671,16 @@ dependencies = [
|
|||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pem"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.1.0"
|
||||
|
@ -1716,14 +1747,6 @@ name = "proc-macro-nested"
|
|||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "0.4.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.7"
|
||||
|
@ -1739,24 +1762,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.14.0"
|
||||
version = "0.17.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"encoding_rs 0.8.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "0.6.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.2"
|
||||
|
@ -1807,7 +1819,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.7.2"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1947,13 +1959,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.3.1"
|
||||
version = "1.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex-syntax 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1963,7 +1975,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.12"
|
||||
version = "0.6.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
|
@ -1985,25 +1997,25 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.14.6"
|
||||
version = "0.16.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"untrusted 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"web-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rss"
|
||||
version = "1.8.0"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"derive_builder 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quick-xml 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"derive_builder 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quick-xml 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2114,7 +2126,7 @@ dependencies = [
|
|||
"lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -2126,7 +2138,7 @@ dependencies = [
|
|||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -2153,7 +2165,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.44"
|
||||
version = "1.0.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -2188,7 +2200,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.8.0"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -2206,6 +2218,16 @@ dependencies = [
|
|||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simple_asn1"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-bigint 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.2"
|
||||
|
@ -2227,6 +2249,11 @@ dependencies = [
|
|||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sourcefile"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
|
@ -2244,7 +2271,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.7.0"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
|
@ -2263,16 +2290,6 @@ dependencies = [
|
|||
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "0.15.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.13"
|
||||
|
@ -2301,7 +2318,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
dependencies = [
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -2352,7 +2369,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "0.3.6"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -2429,7 +2446,7 @@ dependencies = [
|
|||
"idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -2488,11 +2505,6 @@ name = "unicode-segmentation"
|
|||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.0"
|
||||
|
@ -2500,7 +2512,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.6.2"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
|
@ -2574,6 +2586,90 @@ name = "wasi"
|
|||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasm-bindgen-macro 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bumpalo 3.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasm-bindgen-shared 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasm-bindgen-macro-support 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasm-bindgen-backend 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasm-bindgen-shared 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-webidl"
|
||||
version = "0.2.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasm-bindgen-backend 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"js-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasm-bindgen 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasm-bindgen-webidl 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "weedle"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "0.4.0"
|
||||
|
@ -2684,12 +2780,14 @@ dependencies = [
|
|||
"checksum adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2"
|
||||
"checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66"
|
||||
"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d"
|
||||
"checksum anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)" = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c"
|
||||
"checksum arc-swap 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d7b8a9123b8027467bce0099fe556c628a53c8d83df0507084c31e9ba2e39aff"
|
||||
"checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9"
|
||||
"checksum ascii_utils 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "71938f30533e4d95a6d17aa530939da3842c2ab6f4f84b9dae68447e4129f74a"
|
||||
"checksum async-trait 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)" = "c8df72488e87761e772f14ae0c2480396810e51b2c2ade912f97f0f7e5b95e3c"
|
||||
"checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90"
|
||||
"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
|
||||
"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
||||
"checksum awc 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d7601d4d1d7ef2335d6597a41b5fe069f6ab799b85f53565ab390e7b7065aac5"
|
||||
"checksum backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)" = "924c76597f0d9ca25d762c25a4d369d51267536465dc5064bdf0eb073ed477ea"
|
||||
"checksum backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491"
|
||||
|
@ -2705,6 +2803,7 @@ dependencies = [
|
|||
"checksum brotli-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4445dea95f4c2b41cde57cc9fee236ae4dbae88d8fcbdb4750fc1bb5d86aaecd"
|
||||
"checksum brotli2 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0cb036c3eade309815c15ddbacec5b22c4d1f3983a774ab2eac2e3e9ea85568e"
|
||||
"checksum bufstream 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "40e38929add23cdf8a366df9b0e088953150724bcbe5fc330b0d8eb3b328eec8"
|
||||
"checksum bumpalo 3.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5fb8038c1ddc0a5f73787b130f4cc75151e96ed33e417fde765eb5a81e3532f4"
|
||||
"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
|
||||
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
|
||||
"checksum bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "10004c15deb332055f7a4a208190aed362cf9a7c2f6ab70a305fba50e1105f38"
|
||||
|
@ -2721,12 +2820,11 @@ dependencies = [
|
|||
"checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
|
||||
"checksum crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "acec9a3b0b3559f15aee4f90746c4e5e293b701c0f7d3925d24e01645267b68c"
|
||||
"checksum crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4"
|
||||
"checksum darling 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fcfbcb0c5961907597a7d1148e3af036268f2b773886b8bb3eeb1e1281d3d3d6"
|
||||
"checksum darling_core 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6afc018370c3bff3eb51f89256a6bdb18b4fdcda72d577982a14954a7a0b402c"
|
||||
"checksum darling_macro 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c6d8dac1c6f1d29a41c4712b4400f878cb4fcc4c7628f298dd75038e024998d1"
|
||||
"checksum derive_builder 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ac53fa6a3cda160df823a9346442525dcaf1e171999a1cf23e67067e4fd64d4"
|
||||
"checksum derive_builder_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0288a23da9333c246bb18c143426074a6ae96747995c5819d2947b64cd942b37"
|
||||
"checksum derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6d944ac6003ed268757ef1ee686753b57efc5fcf0ebe7b64c9fc81e7e32ff839"
|
||||
"checksum darling 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858"
|
||||
"checksum darling_core 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b"
|
||||
"checksum darling_macro 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72"
|
||||
"checksum derive_builder 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a2658621297f2cf68762a6f7dc0bb7e1ff2cfd6583daef8ee0fed6f7ec468ec0"
|
||||
"checksum derive_builder_core 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2791ea3e372c8495c0bc2033991d76b512cd799d07491fbd6890124db9458bef"
|
||||
"checksum derive_more 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2159be042979966de68315bce7034bb000c775f22e3e834e1c52ff78f041cae8"
|
||||
"checksum diesel 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9d7cc03b910de9935007861dce440881f69102aaaedfd4bc5a6f40340ca5840c"
|
||||
"checksum diesel_derives 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3"
|
||||
|
@ -2787,7 +2885,8 @@ dependencies = [
|
|||
"checksum ipconfig 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa79fa216fbe60834a9c0737d7fcd30425b32d1c58854663e24d4c4b328ed83f"
|
||||
"checksum itoa 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae3088ea4baeceb0284ee9eea42f591226e6beaecf65373e41b38d95a1b8e7a1"
|
||||
"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"
|
||||
"checksum jsonwebtoken 6.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a81d1812d731546d2614737bee92aa071d37e9afa1409bc374da9e5e70e70b22"
|
||||
"checksum js-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)" = "7889c7c36282151f6bf465be4700359318aef36baa951462382eae49e9577cf9"
|
||||
"checksum jsonwebtoken 7.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7577c6272114f9a75da574d2497509d7a73c25cd005a2df35e4a1845a6522ea4"
|
||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
|
||||
"checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73"
|
||||
|
@ -2817,9 +2916,10 @@ dependencies = [
|
|||
"checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
|
||||
"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
|
||||
"checksum nom 5.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c618b63422da4401283884e6668d39f819a106ef51f5f59b81add00075da35ca"
|
||||
"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09"
|
||||
"checksum num-bigint 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304"
|
||||
"checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba"
|
||||
"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31"
|
||||
"checksum num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c81ffc11c212fa327657cb19dd85eb7419e163b5b076bede2bdb5c974c07e4"
|
||||
"checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096"
|
||||
"checksum num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "76dac5ed2a876980778b8b85f75a71b6cbf0db0b1232ee12f826bccb00d09d72"
|
||||
"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
|
||||
"checksum openssl 0.10.26 (registry+https://github.com/rust-lang/crates.io-index)" = "3a3cc5799d98e1088141b8e01ff760112bbd9f19d850c124500566ca6901a585"
|
||||
|
@ -2827,6 +2927,7 @@ dependencies = [
|
|||
"checksum openssl-sys 0.9.53 (registry+https://github.com/rust-lang/crates.io-index)" = "465d16ae7fc0e313318f7de5cecf57b2fbe7511fd213978b457e1c96ff46736f"
|
||||
"checksum parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc"
|
||||
"checksum parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1"
|
||||
"checksum pem 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a1581760c757a756a41f0ee3ff01256227bdf64cb752839779b95ffb01c59793"
|
||||
"checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||
"checksum pin-project 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "94b90146c7216e4cb534069fb91366de4ea0ea353105ee45ed297e2d1619e469"
|
||||
"checksum pin-project-internal 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "44ca92f893f0656d3cba8158dd0f2b99b94de256a4a54e870bd6922fcc6c8355"
|
||||
|
@ -2837,16 +2938,14 @@ dependencies = [
|
|||
"checksum pq-sys 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "6ac25eee5a0582f45a67e837e350d784e7003bd29a5f460796772061ca49ffda"
|
||||
"checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5"
|
||||
"checksum proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e"
|
||||
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
|
||||
"checksum proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0319972dcae462681daf4da1adeeaa066e3ebd29c69be96c6abb1259d2ee2bcc"
|
||||
"checksum quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||
"checksum quick-xml 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0a8b2062cd4735d683121dbd525f5961226936229b0ac6bbbc40b34155744a41"
|
||||
"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
|
||||
"checksum quick-xml 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fe1e430bdcf30c9fdc25053b9c459bb1a4672af4617b6c783d7d91dc17c6bbb0"
|
||||
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
|
||||
"checksum r2d2 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1497e40855348e4a8a40767d8e55174bce1e445a3ac9254ad44ad468ee0485af"
|
||||
"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
|
||||
"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
|
||||
"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412"
|
||||
"checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
|
||||
"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
|
||||
"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853"
|
||||
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
||||
|
@ -2862,13 +2961,13 @@ dependencies = [
|
|||
"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
|
||||
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
|
||||
"checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f"
|
||||
"checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd"
|
||||
"checksum regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "322cf97724bea3ee221b78fe25ac9c46114ebb51747ad5babd51a2fc6a8235a8"
|
||||
"checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957"
|
||||
"checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716"
|
||||
"checksum regex-syntax 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)" = "b28dfe3fe9badec5dbf0a79a9cccad2cfc2ab5484bdb3e44cbd1ae8b3ba2be06"
|
||||
"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
|
||||
"checksum resolv-conf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b263b4aa1b5de9ffc0054a2386f96992058bb6870aab516f8cdeb8a667d56dcb"
|
||||
"checksum ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)" = "426bc186e3e95cac1e4a4be125a4aca7e84c2d616ffc02244eef36e2a60a093c"
|
||||
"checksum rss 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0706a43e890fbaf1714d495d12f69a7b34b70c6e903586d70311c2ce15ffe67"
|
||||
"checksum ring 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6747f8da1f2b1fabbee1aaa4eb8a11abf9adef0bf58a41cee45db5d59cecdfac"
|
||||
"checksum rss 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "99979205510c60f80a119dedbabd0b8426517384edf205322f8bcd51796bcef9"
|
||||
"checksum rust-ini 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2"
|
||||
"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
|
||||
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
|
||||
|
@ -2888,22 +2987,23 @@ dependencies = [
|
|||
"checksum serde-hjson 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6a3a4e0ea8a88553209f6cc6cfe8724ecad22e1acf372793c27d995290fe74f8"
|
||||
"checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64"
|
||||
"checksum serde_json 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)" = "67f7d2e9edc3523a9c8ec8cd6ec481b3a27810aafee3e625d311febd3e656b4c"
|
||||
"checksum serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)" = "48c575e0cc52bdd09b47f330f646cf59afc586e9c4e3ccd6fc1f625b8ea1dad7"
|
||||
"checksum serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "eab8f15f15d6c41a154c1b128a22f2dfabe350ef53c40953d84e36155c91192b"
|
||||
"checksum serde_test 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)" = "110b3dbdf8607ec493c22d5d947753282f3bae73c0f56d322af1e8c78e4c23d5"
|
||||
"checksum serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97"
|
||||
"checksum sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
|
||||
"checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d"
|
||||
"checksum sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "27044adfd2e1f077f649f59deb9490d3941d674002f7d062870a60ebe9bd47a0"
|
||||
"checksum signal-hook-registry 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41"
|
||||
"checksum simple_asn1 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2b25ecba7165254f0c97d6c22a64b1122a03634b18d20a34daf21e18f892e618"
|
||||
"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
|
||||
"checksum smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44e59e0c9fa00817912ae6e4e6e3c4fe04455e75699d06eedc7d85917ed8e8f4"
|
||||
"checksum socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "e8b74de517221a2cb01a53349cf54182acdc31a074727d3079068448c0676d85"
|
||||
"checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3"
|
||||
"checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
"checksum static_assertions 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3"
|
||||
"checksum strsim 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "67f84c44fbb2f91db7fef94554e6b2ac05909c9c0b0bc23bb98d3a1aebfe7f7c"
|
||||
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
|
||||
"checksum strsim 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
|
||||
"checksum strum 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)" = "530efb820d53b712f4e347916c5e7ed20deb76a4f0457943b3182fb889b06d2c"
|
||||
"checksum strum_macros 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5e6e163a520367c465f59e0a61a23cfae3b10b6546d78b6f672a382be79f7110"
|
||||
"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
|
||||
"checksum syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1e4ff033220a41d1a57d8125eab57bf5263783dfdcc18688b1dacc6ce9651ef8"
|
||||
"checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545"
|
||||
"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
|
||||
|
@ -2912,7 +3012,7 @@ dependencies = [
|
|||
"checksum thiserror-impl 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2e25d25307eb8436894f727aba8f65d07adf02e5b35a13cebed48bd282bfef"
|
||||
"checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03"
|
||||
"checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5"
|
||||
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
|
||||
"checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
|
||||
"checksum threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e2f0c90a5f3459330ac8bc0d2f879c693bb7a2f59689c1083fc4ef83834da865"
|
||||
"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
|
||||
"checksum tokio 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "ffa2fdcfa937b20cb3c822a635ceecd5fc1a27a6a474527e5516aa24b8c8820a"
|
||||
|
@ -2925,9 +3025,8 @@ dependencies = [
|
|||
"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
|
||||
"checksum unicode-normalization 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b561e267b2326bb4cebfc0ef9e68355c7abe6c6f522aeac2f5bf95d56c59bdcf"
|
||||
"checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
|
||||
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
||||
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
||||
"checksum untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "55cd1f4b4e96b46aeb8d4855db4a7a9bd96eeeb5c6a1ab54593328761642ce2f"
|
||||
"checksum untrusted 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60369ef7a31de49bcb3f6ca728d4ba7300d9a1658f94c727d4cab8c8d9f4aece"
|
||||
"checksum url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75b414f6c464c879d7f9babf951f23bc3743fb7313c081b2e6ca719067ea9d61"
|
||||
"checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f"
|
||||
"checksum uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90dbc611eb48397705a6b0f6e917da23ae517e4d127123d2cf7674206627d32a"
|
||||
|
@ -2938,6 +3037,14 @@ dependencies = [
|
|||
"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
|
||||
"checksum version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce"
|
||||
"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d"
|
||||
"checksum wasm-bindgen 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "5205e9afdf42282b192e2310a5b463a6d1c1d774e30dc3c791ac37ab42d2616c"
|
||||
"checksum wasm-bindgen-backend 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "11cdb95816290b525b32587d76419facd99662a07e59d3cdb560488a819d9a45"
|
||||
"checksum wasm-bindgen-macro 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "574094772ce6921576fb6f2e3f7497b8a76273b6db092be18fc48a082de09dc3"
|
||||
"checksum wasm-bindgen-macro-support 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "e85031354f25eaebe78bb7db1c3d86140312a911a106b2e29f9cc440ce3e7668"
|
||||
"checksum wasm-bindgen-shared 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "f5e7e61fc929f4c0dddb748b102ebf9f632e2b8d739f2016542b4de2965a9601"
|
||||
"checksum wasm-bindgen-webidl 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "ef012a0d93fc0432df126a8eaf547b2dce25a8ce9212e1d3cbeef5c11157975d"
|
||||
"checksum web-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)" = "aaf97caf6aa8c2b1dac90faf0db529d9d63c93846cca4911856f78a83cebf53b"
|
||||
"checksum weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bb43f70885151e629e2a19ce9e50bd730fd436cfd4b666894c9ce4de9141164"
|
||||
"checksum widestring 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "effc0e4ff8085673ea7b9b2e3c73f6bd4d118810c9009ed8f1e16bd96c331db6"
|
||||
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
|
||||
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
|
||||
|
|
12
server/Cargo.toml
vendored
12
server/Cargo.toml
vendored
|
@ -12,7 +12,7 @@ bcrypt = "0.6.1"
|
|||
activitypub = "0.2.0"
|
||||
chrono = { version = "0.4.7", features = ["serde"] }
|
||||
failure = "0.1.5"
|
||||
serde_json = { version = "1.0.40", features = ["preserve_order"]}
|
||||
serde_json = { version = "1.0.45", features = ["preserve_order"]}
|
||||
serde = { version = "1.0.94", features = ["derive"] }
|
||||
actix = "0.9.0"
|
||||
actix-web = "2.0.0"
|
||||
|
@ -20,16 +20,16 @@ actix-files = "0.2.1"
|
|||
actix-web-actors = "2.0.0"
|
||||
actix-rt = "1.0.0"
|
||||
env_logger = "0.7.1"
|
||||
rand = "0.7.0"
|
||||
rand = "0.7.3"
|
||||
strum = "0.17.1"
|
||||
strum_macros = "0.17.1"
|
||||
jsonwebtoken = "6.0.1"
|
||||
regex = "1.1.9"
|
||||
jsonwebtoken = "7.0.1"
|
||||
regex = "1.3.4"
|
||||
lazy_static = "1.3.0"
|
||||
lettre = "0.9.2"
|
||||
lettre_email = "0.9.2"
|
||||
sha2 = "0.8.0"
|
||||
rss = "1.8.0"
|
||||
sha2 = "0.8.1"
|
||||
rss = "1.9.0"
|
||||
htmlescape = "0.3.1"
|
||||
config = "0.10.1"
|
||||
hjson = "0.8.2"
|
||||
|
|
2
server/migrations/2020-02-02-004806_add_case_insensitive_usernames/down.sql
vendored
Normal file
2
server/migrations/2020-02-02-004806_add_case_insensitive_usernames/down.sql
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
drop index idx_user_name_lower;
|
||||
drop index idx_user_email_lower;
|
29
server/migrations/2020-02-02-004806_add_case_insensitive_usernames/up.sql
vendored
Normal file
29
server/migrations/2020-02-02-004806_add_case_insensitive_usernames/up.sql
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
-- Add case insensitive username and email uniqueness
|
||||
|
||||
-- An example of showing the dupes:
|
||||
-- select
|
||||
-- max(id) as id,
|
||||
-- lower(name) as lname,
|
||||
-- count(*)
|
||||
-- from user_
|
||||
-- group by lower(name)
|
||||
-- having count(*) > 1;
|
||||
|
||||
-- Delete username dupes, keeping the first one
|
||||
delete
|
||||
from user_
|
||||
where id not in (
|
||||
select min(id)
|
||||
from user_
|
||||
group by lower(name), lower(fedi_name)
|
||||
);
|
||||
|
||||
-- The user index
|
||||
create unique index idx_user_name_lower on user_ (lower(name));
|
||||
|
||||
-- Email lower
|
||||
create unique index idx_user_email_lower on user_ (lower(email));
|
||||
|
||||
-- Set empty emails properly to null
|
||||
update user_ set email = null where email = '';
|
||||
|
3
server/query_testing/apache_bench_report.sh
vendored
3
server/query_testing/apache_bench_report.sh
vendored
|
@ -1,4 +1,5 @@
|
|||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
declare -a arr=(
|
||||
"https://mastodon.social/"
|
||||
|
|
34
server/query_testing/api_benchmark.sh
vendored
Executable file
34
server/query_testing/api_benchmark.sh
vendored
Executable file
|
@ -0,0 +1,34 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# By default, this script runs against `http://127.0.0.1:8536`, but you can pass a different Lemmy instance,
|
||||
# eg `./api_benchmark.sh "https://example.com"`.
|
||||
DOMAIN=${1:-"http://127.0.0.1:8536"}
|
||||
|
||||
declare -a arr=(
|
||||
"/api/v1/site"
|
||||
"/api/v1/categories"
|
||||
"/api/v1/modlog"
|
||||
"/api/v1/search?q=test&type_=Posts&sort=Hot"
|
||||
"/api/v1/community"
|
||||
"/api/v1/community/list?sort=Hot"
|
||||
"/api/v1/post/list?sort=Hot&type_=All"
|
||||
)
|
||||
|
||||
## now loop through the above array
|
||||
for path in "${arr[@]}"
|
||||
do
|
||||
URL="$DOMAIN$path"
|
||||
printf "\n\n\n"
|
||||
echo "testing $URL"
|
||||
curl --show-error --fail --silent "$URL" >/dev/null
|
||||
ab -c 64 -t 10 "$URL" > out.abtest
|
||||
grep "Server Hostname:" out.abtest
|
||||
grep "Document Path:" out.abtest
|
||||
grep "Requests per second" out.abtest
|
||||
grep "(mean, across all concurrent requests)" out.abtest
|
||||
grep "Transfer rate:" out.abtest
|
||||
echo "---"
|
||||
done
|
||||
|
||||
rm *.abtest
|
|
@ -1,4 +1,5 @@
|
|||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Do the views first
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ pub struct SaveComment {
|
|||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct CommentResponse {
|
||||
pub comment: CommentView,
|
||||
pub recipient_ids: Vec<i32>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
|
@ -88,6 +89,8 @@ impl Perform<CommentResponse> for Oper<CreateComment> {
|
|||
Err(_e) => return Err(APIError::err("couldnt_create_comment").into()),
|
||||
};
|
||||
|
||||
let mut recipient_ids = Vec::new();
|
||||
|
||||
// Scan the comment for user mentions, add those rows
|
||||
let extracted_usernames = extract_usernames(&comment_form.content);
|
||||
|
||||
|
@ -97,6 +100,8 @@ impl Perform<CommentResponse> for Oper<CreateComment> {
|
|||
// At some point, make it so you can't tag the parent creator either
|
||||
// This can cause two notifications, one for reply and the other for mention
|
||||
if mention_user.id != user_id {
|
||||
recipient_ids.push(mention_user.id);
|
||||
|
||||
let user_mention_form = UserMentionForm {
|
||||
recipient_id: mention_user.id,
|
||||
comment_id: inserted_comment.id,
|
||||
|
@ -138,6 +143,8 @@ impl Perform<CommentResponse> for Oper<CreateComment> {
|
|||
let parent_comment = Comment::read(&conn, parent_id)?;
|
||||
if parent_comment.creator_id != user_id {
|
||||
let parent_user = User_::read(&conn, parent_comment.creator_id)?;
|
||||
recipient_ids.push(parent_user.id);
|
||||
|
||||
if parent_user.send_notifications_to_email {
|
||||
if let Some(comment_reply_email) = parent_user.email {
|
||||
let subject = &format!(
|
||||
|
@ -161,6 +168,8 @@ impl Perform<CommentResponse> for Oper<CreateComment> {
|
|||
None => {
|
||||
if post.creator_id != user_id {
|
||||
let parent_user = User_::read(&conn, post.creator_id)?;
|
||||
recipient_ids.push(parent_user.id);
|
||||
|
||||
if parent_user.send_notifications_to_email {
|
||||
if let Some(post_reply_email) = parent_user.email {
|
||||
let subject = &format!(
|
||||
|
@ -199,6 +208,7 @@ impl Perform<CommentResponse> for Oper<CreateComment> {
|
|||
|
||||
Ok(CommentResponse {
|
||||
comment: comment_view,
|
||||
recipient_ids,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -265,6 +275,8 @@ impl Perform<CommentResponse> for Oper<EditComment> {
|
|||
Err(_e) => return Err(APIError::err("couldnt_update_comment").into()),
|
||||
};
|
||||
|
||||
let mut recipient_ids = Vec::new();
|
||||
|
||||
// Scan the comment for user mentions, add those rows
|
||||
let extracted_usernames = extract_usernames(&comment_form.content);
|
||||
|
||||
|
@ -278,6 +290,8 @@ impl Perform<CommentResponse> for Oper<EditComment> {
|
|||
// At some point, make it so you can't tag the parent creator either
|
||||
// This can cause two notifications, one for reply and the other for mention
|
||||
if mention_user_id != user_id {
|
||||
recipient_ids.push(mention_user_id);
|
||||
|
||||
let user_mention_form = UserMentionForm {
|
||||
recipient_id: mention_user_id,
|
||||
comment_id: data.edit_id,
|
||||
|
@ -294,6 +308,21 @@ impl Perform<CommentResponse> for Oper<EditComment> {
|
|||
}
|
||||
}
|
||||
|
||||
// Add to recipient ids
|
||||
match data.parent_id {
|
||||
Some(parent_id) => {
|
||||
let parent_comment = Comment::read(&conn, parent_id)?;
|
||||
if parent_comment.creator_id != user_id {
|
||||
let parent_user = User_::read(&conn, parent_comment.creator_id)?;
|
||||
recipient_ids.push(parent_user.id);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let post = Post::read(&conn, data.post_id)?;
|
||||
recipient_ids.push(post.creator_id);
|
||||
}
|
||||
}
|
||||
|
||||
// Mod tables
|
||||
if let Some(removed) = data.removed.to_owned() {
|
||||
let form = ModRemoveCommentForm {
|
||||
|
@ -309,6 +338,7 @@ impl Perform<CommentResponse> for Oper<EditComment> {
|
|||
|
||||
Ok(CommentResponse {
|
||||
comment: comment_view,
|
||||
recipient_ids,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -345,6 +375,7 @@ impl Perform<CommentResponse> for Oper<SaveComment> {
|
|||
|
||||
Ok(CommentResponse {
|
||||
comment: comment_view,
|
||||
recipient_ids: Vec::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -360,6 +391,8 @@ impl Perform<CommentResponse> for Oper<CreateCommentLike> {
|
|||
|
||||
let user_id = claims.id;
|
||||
|
||||
let mut recipient_ids = Vec::new();
|
||||
|
||||
// Don't do a downvote if site has downvotes disabled
|
||||
if data.score == -1 {
|
||||
let site = SiteView::read(&conn)?;
|
||||
|
@ -379,6 +412,22 @@ impl Perform<CommentResponse> for Oper<CreateCommentLike> {
|
|||
return Err(APIError::err("site_ban").into());
|
||||
}
|
||||
|
||||
let comment = Comment::read(&conn, data.comment_id)?;
|
||||
|
||||
// Add to recipient ids
|
||||
match comment.parent_id {
|
||||
Some(parent_id) => {
|
||||
let parent_comment = Comment::read(&conn, parent_id)?;
|
||||
if parent_comment.creator_id != user_id {
|
||||
let parent_user = User_::read(&conn, parent_comment.creator_id)?;
|
||||
recipient_ids.push(parent_user.id);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
recipient_ids.push(post.creator_id);
|
||||
}
|
||||
}
|
||||
|
||||
let like_form = CommentLikeForm {
|
||||
comment_id: data.comment_id,
|
||||
post_id: data.post_id,
|
||||
|
@ -403,6 +452,7 @@ impl Perform<CommentResponse> for Oper<CreateCommentLike> {
|
|||
|
||||
Ok(CommentResponse {
|
||||
comment: liked_comment,
|
||||
recipient_ids,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,9 +11,10 @@ pub struct GetCommunity {
|
|||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct GetCommunityResponse {
|
||||
community: CommunityView,
|
||||
pub community: CommunityView,
|
||||
moderators: Vec<CommunityModeratorView>,
|
||||
admins: Vec<UserView>,
|
||||
pub online: usize,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
|
@ -161,6 +162,7 @@ impl Perform<GetCommunityResponse> for Oper<GetCommunity> {
|
|||
community: community_view,
|
||||
moderators,
|
||||
admins,
|
||||
online: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -174,11 +176,18 @@ impl Perform<CommunityResponse> for Oper<CreateCommunity> {
|
|||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
||||
};
|
||||
|
||||
if has_slurs(&data.name)
|
||||
|| has_slurs(&data.title)
|
||||
|| (data.description.is_some() && has_slurs(&data.description.to_owned().unwrap()))
|
||||
{
|
||||
return Err(APIError::err("no_slurs").into());
|
||||
if let Err(slurs) = slur_check(&data.name) {
|
||||
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
|
||||
}
|
||||
|
||||
if let Err(slurs) = slur_check(&data.title) {
|
||||
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
|
||||
}
|
||||
|
||||
if let Some(description) = &data.description {
|
||||
if let Err(slurs) = slur_check(description) {
|
||||
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
|
||||
}
|
||||
}
|
||||
|
||||
let user_id = claims.id;
|
||||
|
@ -240,8 +249,18 @@ impl Perform<CommunityResponse> for Oper<EditCommunity> {
|
|||
fn perform(&self, conn: &PgConnection) -> Result<CommunityResponse, Error> {
|
||||
let data: &EditCommunity = &self.data;
|
||||
|
||||
if has_slurs(&data.name) || has_slurs(&data.title) {
|
||||
return Err(APIError::err("no_slurs").into());
|
||||
if let Err(slurs) = slur_check(&data.name) {
|
||||
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
|
||||
}
|
||||
|
||||
if let Err(slurs) = slur_check(&data.title) {
|
||||
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
|
||||
}
|
||||
|
||||
if let Some(description) = &data.description {
|
||||
if let Err(slurs) = slur_check(description) {
|
||||
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
|
||||
}
|
||||
}
|
||||
|
||||
let claims = match Claims::decode(&data.auth) {
|
||||
|
@ -590,6 +609,7 @@ impl Perform<GetCommunityResponse> for Oper<TransferCommunity> {
|
|||
community: community_view,
|
||||
moderators,
|
||||
admins,
|
||||
online: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,9 @@ use crate::db::user_mention::*;
|
|||
use crate::db::user_mention_view::*;
|
||||
use crate::db::user_view::*;
|
||||
use crate::db::*;
|
||||
use crate::{extract_usernames, has_slurs, naive_from_unix, naive_now, remove_slurs};
|
||||
use crate::{
|
||||
extract_usernames, naive_from_unix, naive_now, remove_slurs, slur_check, slurs_vec_to_str,
|
||||
};
|
||||
use diesel::PgConnection;
|
||||
use failure::Error;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -8,7 +8,7 @@ pub struct CreatePost {
|
|||
url: Option<String>,
|
||||
body: Option<String>,
|
||||
nsfw: bool,
|
||||
community_id: i32,
|
||||
pub community_id: i32,
|
||||
auth: String,
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,7 @@ pub struct GetPostResponse {
|
|||
community: CommunityView,
|
||||
moderators: Vec<CommunityModeratorView>,
|
||||
admins: Vec<UserView>,
|
||||
pub online: usize,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
|
@ -38,7 +39,7 @@ pub struct GetPosts {
|
|||
sort: String,
|
||||
page: Option<i64>,
|
||||
limit: Option<i64>,
|
||||
community_id: Option<i32>,
|
||||
pub community_id: Option<i32>,
|
||||
auth: Option<String>,
|
||||
}
|
||||
|
||||
|
@ -54,11 +55,6 @@ pub struct CreatePostLike {
|
|||
auth: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct CreatePostLikeResponse {
|
||||
post: PostView,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct EditPost {
|
||||
pub edit_id: i32,
|
||||
|
@ -92,8 +88,14 @@ impl Perform<PostResponse> for Oper<CreatePost> {
|
|||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
||||
};
|
||||
|
||||
if has_slurs(&data.name) || (data.body.is_some() && has_slurs(&data.body.to_owned().unwrap())) {
|
||||
return Err(APIError::err("no_slurs").into());
|
||||
if let Err(slurs) = slur_check(&data.name) {
|
||||
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
|
||||
}
|
||||
|
||||
if let Some(body) = &data.body {
|
||||
if let Err(slurs) = slur_check(body) {
|
||||
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
|
||||
}
|
||||
}
|
||||
|
||||
let user_id = claims.id;
|
||||
|
@ -193,6 +195,7 @@ impl Perform<GetPostResponse> for Oper<GetPost> {
|
|||
community,
|
||||
moderators,
|
||||
admins,
|
||||
online: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -240,8 +243,8 @@ impl Perform<GetPostsResponse> for Oper<GetPosts> {
|
|||
}
|
||||
}
|
||||
|
||||
impl Perform<CreatePostLikeResponse> for Oper<CreatePostLike> {
|
||||
fn perform(&self, conn: &PgConnection) -> Result<CreatePostLikeResponse, Error> {
|
||||
impl Perform<PostResponse> for Oper<CreatePostLike> {
|
||||
fn perform(&self, conn: &PgConnection) -> Result<PostResponse, Error> {
|
||||
let data: &CreatePostLike = &self.data;
|
||||
|
||||
let claims = match Claims::decode(&data.auth) {
|
||||
|
@ -294,15 +297,22 @@ impl Perform<CreatePostLikeResponse> for Oper<CreatePostLike> {
|
|||
};
|
||||
|
||||
// just output the score
|
||||
Ok(CreatePostLikeResponse { post: post_view })
|
||||
Ok(PostResponse { post: post_view })
|
||||
}
|
||||
}
|
||||
|
||||
impl Perform<PostResponse> for Oper<EditPost> {
|
||||
fn perform(&self, conn: &PgConnection) -> Result<PostResponse, Error> {
|
||||
let data: &EditPost = &self.data;
|
||||
if has_slurs(&data.name) || (data.body.is_some() && has_slurs(&data.body.to_owned().unwrap())) {
|
||||
return Err(APIError::err("no_slurs").into());
|
||||
|
||||
if let Err(slurs) = slur_check(&data.name) {
|
||||
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
|
||||
}
|
||||
|
||||
if let Some(body) = &data.body {
|
||||
if let Err(slurs) = slur_check(body) {
|
||||
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
|
||||
}
|
||||
}
|
||||
|
||||
let claims = match Claims::decode(&data.auth) {
|
||||
|
|
|
@ -3,7 +3,7 @@ use diesel::PgConnection;
|
|||
use std::str::FromStr;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ListCategories;
|
||||
pub struct ListCategories {}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ListCategoriesResponse {
|
||||
|
@ -72,7 +72,7 @@ pub struct EditSite {
|
|||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct GetSite;
|
||||
pub struct GetSite {}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct SiteResponse {
|
||||
|
@ -186,10 +186,14 @@ impl Perform<SiteResponse> for Oper<CreateSite> {
|
|||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
||||
};
|
||||
|
||||
if has_slurs(&data.name)
|
||||
|| (data.description.is_some() && has_slurs(&data.description.to_owned().unwrap()))
|
||||
{
|
||||
return Err(APIError::err("no_slurs").into());
|
||||
if let Err(slurs) = slur_check(&data.name) {
|
||||
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
|
||||
}
|
||||
|
||||
if let Some(description) = &data.description {
|
||||
if let Err(slurs) = slur_check(description) {
|
||||
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
|
||||
}
|
||||
}
|
||||
|
||||
let user_id = claims.id;
|
||||
|
@ -229,10 +233,14 @@ impl Perform<SiteResponse> for Oper<EditSite> {
|
|||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
||||
};
|
||||
|
||||
if has_slurs(&data.name)
|
||||
|| (data.description.is_some() && has_slurs(&data.description.to_owned().unwrap()))
|
||||
{
|
||||
return Err(APIError::err("no_slurs").into());
|
||||
if let Err(slurs) = slur_check(&data.name) {
|
||||
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
|
||||
}
|
||||
|
||||
if let Some(description) = &data.description {
|
||||
if let Err(slurs) = slur_check(description) {
|
||||
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
|
||||
}
|
||||
}
|
||||
|
||||
let user_id = claims.id;
|
||||
|
|
|
@ -162,7 +162,7 @@ pub struct PasswordChange {
|
|||
#[derive(Serialize, Deserialize)]
|
||||
pub struct CreatePrivateMessage {
|
||||
content: String,
|
||||
recipient_id: i32,
|
||||
pub recipient_id: i32,
|
||||
auth: String,
|
||||
}
|
||||
|
||||
|
@ -193,6 +193,16 @@ pub struct PrivateMessageResponse {
|
|||
message: PrivateMessageView,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct UserJoin {
|
||||
auth: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct UserJoinResponse {
|
||||
pub user_id: i32,
|
||||
}
|
||||
|
||||
impl Perform<LoginResponse> for Oper<Login> {
|
||||
fn perform(&self, conn: &PgConnection) -> Result<LoginResponse, Error> {
|
||||
let data: &Login = &self.data;
|
||||
|
@ -230,8 +240,8 @@ impl Perform<LoginResponse> for Oper<Register> {
|
|||
return Err(APIError::err("passwords_dont_match").into());
|
||||
}
|
||||
|
||||
if has_slurs(&data.username) {
|
||||
return Err(APIError::err("no_slurs").into());
|
||||
if let Err(slurs) = slur_check(&data.username) {
|
||||
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
|
||||
}
|
||||
|
||||
// Make sure there are no admins
|
||||
|
@ -1071,3 +1081,17 @@ impl Perform<PrivateMessagesResponse> for Oper<GetPrivateMessages> {
|
|||
Ok(PrivateMessagesResponse { messages })
|
||||
}
|
||||
}
|
||||
|
||||
impl Perform<UserJoinResponse> for Oper<UserJoin> {
|
||||
fn perform(&self, _conn: &PgConnection) -> Result<UserJoinResponse, Error> {
|
||||
let data: &UserJoin = &self.data;
|
||||
|
||||
let claims = match Claims::decode(&data.auth) {
|
||||
Ok(claims) => claims.claims,
|
||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
||||
};
|
||||
|
||||
let user_id = claims.id;
|
||||
Ok(UserJoinResponse { user_id })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -348,7 +348,9 @@ impl<'a> ReplyQueryBuilder<'a> {
|
|||
|
||||
query = query
|
||||
.filter(user_id.eq(self.for_user_id))
|
||||
.filter(recipient_id.eq(self.for_user_id));
|
||||
.filter(recipient_id.eq(self.for_user_id))
|
||||
.filter(deleted.eq(false))
|
||||
.filter(removed.eq(false));
|
||||
|
||||
if self.unread_only {
|
||||
query = query.filter(read.eq(false));
|
||||
|
|
|
@ -98,7 +98,7 @@ impl<'a> PrivateMessageQueryBuilder<'a> {
|
|||
pub fn list(self) -> Result<Vec<PrivateMessageView>, Error> {
|
||||
use super::private_message_view::private_message_mview::dsl::*;
|
||||
|
||||
let mut query = self.query;
|
||||
let mut query = self.query.filter(deleted.eq(false));
|
||||
|
||||
// If its unread, I only want the ones to me
|
||||
if self.unread_only {
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::schema::user_;
|
|||
use crate::schema::user_::dsl::*;
|
||||
use crate::{is_email_regex, Settings};
|
||||
use bcrypt::{hash, DEFAULT_COST};
|
||||
use jsonwebtoken::{decode, encode, Header, TokenData, Validation};
|
||||
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, TokenData, Validation};
|
||||
|
||||
#[derive(Queryable, Identifiable, PartialEq, Debug)]
|
||||
#[table_name = "user_"]
|
||||
|
@ -115,7 +115,11 @@ impl Claims {
|
|||
validate_exp: false,
|
||||
..Validation::default()
|
||||
};
|
||||
decode::<Claims>(&jwt, Settings::get().jwt_secret.as_ref(), &v)
|
||||
decode::<Claims>(
|
||||
&jwt,
|
||||
&DecodingKey::from_secret(Settings::get().jwt_secret.as_ref()),
|
||||
&v,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,7 +141,7 @@ impl User_ {
|
|||
encode(
|
||||
&Header::default(),
|
||||
&my_claims,
|
||||
Settings::get().jwt_secret.as_ref(),
|
||||
&EncodingKey::from_secret(Settings::get().jwt_secret.as_ref()),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ use chrono::{DateTime, NaiveDateTime, Utc};
|
|||
use lettre::smtp::authentication::{Credentials, Mechanism};
|
||||
use lettre::smtp::extension::ClientId;
|
||||
use lettre::smtp::ConnectionReuseParameters;
|
||||
use lettre::{SmtpClient, Transport};
|
||||
use lettre::{ClientSecurity, SmtpClient, Transport};
|
||||
use lettre_email::Email;
|
||||
use rand::distributions::Alphanumeric;
|
||||
use rand::{thread_rng, Rng};
|
||||
|
@ -62,8 +62,24 @@ pub fn remove_slurs(test: &str) -> String {
|
|||
SLUR_REGEX.replace_all(test, "*removed*").to_string()
|
||||
}
|
||||
|
||||
pub fn has_slurs(test: &str) -> bool {
|
||||
SLUR_REGEX.is_match(test)
|
||||
pub fn slur_check(test: &str) -> Result<(), Vec<&str>> {
|
||||
let mut matches: Vec<&str> = SLUR_REGEX.find_iter(test).map(|mat| mat.as_str()).collect();
|
||||
|
||||
// Unique
|
||||
matches.sort_unstable();
|
||||
matches.dedup();
|
||||
|
||||
if matches.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(matches)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn slurs_vec_to_str(slurs: Vec<&str>) -> String {
|
||||
let start = "No slurs - ";
|
||||
let combined = &slurs.join(", ");
|
||||
[start, combined].concat()
|
||||
}
|
||||
|
||||
pub fn extract_usernames(test: &str) -> Vec<&str> {
|
||||
|
@ -94,40 +110,42 @@ pub fn send_email(
|
|||
|
||||
let email = Email::builder()
|
||||
.to((to_email, to_username))
|
||||
.from((
|
||||
email_config.smtp_login.to_owned(),
|
||||
email_config.smtp_from_address.to_owned(),
|
||||
))
|
||||
.from(email_config.smtp_from_address.to_owned())
|
||||
.subject(subject)
|
||||
.html(html)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let mut mailer = SmtpClient::new_simple(&email_config.smtp_server)
|
||||
.unwrap()
|
||||
let mailer = if email_config.use_tls {
|
||||
SmtpClient::new_simple(&email_config.smtp_server).unwrap()
|
||||
} else {
|
||||
SmtpClient::new(&email_config.smtp_server, ClientSecurity::None).unwrap()
|
||||
}
|
||||
.hello_name(ClientId::Domain(Settings::get().hostname.to_owned()))
|
||||
.credentials(Credentials::new(
|
||||
email_config.smtp_login.to_owned(),
|
||||
email_config.smtp_password.to_owned(),
|
||||
))
|
||||
.smtp_utf8(true)
|
||||
.authentication_mechanism(Mechanism::Plain)
|
||||
.connection_reuse(ConnectionReuseParameters::ReuseUnlimited)
|
||||
.transport();
|
||||
.connection_reuse(ConnectionReuseParameters::ReuseUnlimited);
|
||||
let mailer = if let (Some(login), Some(password)) =
|
||||
(&email_config.smtp_login, &email_config.smtp_password)
|
||||
{
|
||||
mailer.credentials(Credentials::new(login.to_owned(), password.to_owned()))
|
||||
} else {
|
||||
mailer
|
||||
};
|
||||
|
||||
let result = mailer.send(email.into());
|
||||
|
||||
mailer.close();
|
||||
let mut transport = mailer.transport();
|
||||
let result = transport.send(email.into());
|
||||
transport.close();
|
||||
|
||||
match result {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err("no_email_setup".to_string()),
|
||||
Err(e) => Err(e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{extract_usernames, has_slurs, is_email_regex, remove_slurs};
|
||||
use crate::{extract_usernames, is_email_regex, remove_slurs, slur_check, slurs_vec_to_str};
|
||||
|
||||
#[test]
|
||||
fn test_email() {
|
||||
|
@ -138,15 +156,29 @@ mod tests {
|
|||
#[test]
|
||||
fn test_slur_filter() {
|
||||
let test =
|
||||
"coons test dindu ladyboy tranny retardeds. Capitalized Niggerz. This is a bunch of other safe text.".to_string();
|
||||
"coons test dindu ladyboy tranny retardeds. Capitalized Niggerz. This is a bunch of other safe text.";
|
||||
let slur_free = "No slurs here";
|
||||
assert_eq!(
|
||||
remove_slurs(&test),
|
||||
"*removed* test *removed* *removed* *removed* *removed*. Capitalized *removed*. This is a bunch of other safe text."
|
||||
.to_string()
|
||||
);
|
||||
assert!(has_slurs(&test));
|
||||
assert!(!has_slurs(slur_free));
|
||||
|
||||
let has_slurs_vec = vec![
|
||||
"Niggerz",
|
||||
"coons",
|
||||
"dindu",
|
||||
"ladyboy",
|
||||
"retardeds",
|
||||
"tranny",
|
||||
];
|
||||
let has_slurs_err_str = "No slurs - Niggerz, coons, dindu, ladyboy, retardeds, tranny";
|
||||
|
||||
assert_eq!(slur_check(test), Err(has_slurs_vec));
|
||||
assert_eq!(slur_check(slur_free), Ok(()));
|
||||
if let Err(slur_vec) = slur_check(test) {
|
||||
assert_eq!(&slurs_vec_to_str(slur_vec), has_slurs_err_str);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -31,7 +31,7 @@ pub fn config(cfg: &mut web::ServiceConfig) {
|
|||
.route("/api/v1/post", web::put().to(route_post::<EditPost, PostResponse>))
|
||||
.route("/api/v1/post", web::get().to(route_get::<GetPost, GetPostResponse>))
|
||||
.route("/api/v1/post/list", web::get().to(route_get::<GetPosts, GetPostsResponse>))
|
||||
.route("/api/v1/post/like", web::post().to(route_post::<CreatePostLike, CreatePostLikeResponse>))
|
||||
.route("/api/v1/post/like", web::post().to(route_post::<CreatePostLike, PostResponse>))
|
||||
.route("/api/v1/post/save", web::put().to(route_post::<SavePost, PostResponse>))
|
||||
// Comment
|
||||
.route("/api/v1/comment", web::post().to(route_post::<CreateComment, CommentResponse>))
|
||||
|
|
|
@ -33,9 +33,10 @@ pub struct RateLimitConfig {
|
|||
#[derive(Debug, Deserialize)]
|
||||
pub struct EmailConfig {
|
||||
pub smtp_server: String,
|
||||
pub smtp_login: String,
|
||||
pub smtp_password: String,
|
||||
pub smtp_login: Option<String>,
|
||||
pub smtp_password: Option<String>,
|
||||
pub smtp_from_address: String,
|
||||
pub use_tls: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
|
|
|
@ -1 +1 @@
|
|||
pub const VERSION: &str = "v0.6.7";
|
||||
pub const VERSION: &str = "v0.6.10";
|
||||
|
|
|
@ -44,4 +44,5 @@ pub enum UserOperation {
|
|||
CreatePrivateMessage,
|
||||
EditPrivateMessage,
|
||||
GetPrivateMessages,
|
||||
UserJoin,
|
||||
}
|
||||
|
|
|
@ -22,6 +22,12 @@ use crate::api::*;
|
|||
use crate::websocket::UserOperation;
|
||||
use crate::Settings;
|
||||
|
||||
type ConnectionId = usize;
|
||||
type PostId = i32;
|
||||
type CommunityId = i32;
|
||||
type UserId = i32;
|
||||
type IPAddr = String;
|
||||
|
||||
/// Chat server sends this messages to session
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
|
@ -34,35 +40,22 @@ pub struct WSMessage(pub String);
|
|||
#[rtype(usize)]
|
||||
pub struct Connect {
|
||||
pub addr: Recipient<WSMessage>,
|
||||
pub ip: String,
|
||||
pub ip: IPAddr,
|
||||
}
|
||||
|
||||
/// Session is disconnected
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct Disconnect {
|
||||
pub id: usize,
|
||||
pub ip: String,
|
||||
}
|
||||
|
||||
// TODO this is unused rn
|
||||
/// Send message to specific room
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct ClientMessage {
|
||||
/// Id of the client session
|
||||
pub id: usize,
|
||||
/// Peer message
|
||||
pub msg: String,
|
||||
/// Room name
|
||||
pub room: String,
|
||||
pub id: ConnectionId,
|
||||
pub ip: IPAddr,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Message)]
|
||||
#[rtype(String)]
|
||||
pub struct StandardMessage {
|
||||
/// Id of the client session
|
||||
pub id: usize,
|
||||
pub id: ConnectionId,
|
||||
/// Peer message
|
||||
pub msg: String,
|
||||
}
|
||||
|
@ -75,36 +68,93 @@ pub struct RateLimitBucket {
|
|||
|
||||
pub struct SessionInfo {
|
||||
pub addr: Recipient<WSMessage>,
|
||||
pub ip: String,
|
||||
pub ip: IPAddr,
|
||||
}
|
||||
|
||||
/// `ChatServer` manages chat rooms and responsible for coordinating chat
|
||||
/// session. implementation is super primitive
|
||||
/// session.
|
||||
pub struct ChatServer {
|
||||
sessions: HashMap<usize, SessionInfo>, // A map from generated random ID to session addr
|
||||
rate_limits: HashMap<String, RateLimitBucket>,
|
||||
rooms: HashMap<i32, HashSet<usize>>, // A map from room / post name to set of connectionIDs
|
||||
/// A map from generated random ID to session addr
|
||||
sessions: HashMap<ConnectionId, SessionInfo>,
|
||||
|
||||
/// A map from post_id to set of connectionIDs
|
||||
post_rooms: HashMap<PostId, HashSet<ConnectionId>>,
|
||||
|
||||
/// A map from community to set of connectionIDs
|
||||
community_rooms: HashMap<CommunityId, HashSet<ConnectionId>>,
|
||||
|
||||
/// A map from user id to its connection ID for joined users. Remember a user can have multiple
|
||||
/// sessions (IE clients)
|
||||
user_rooms: HashMap<UserId, HashSet<ConnectionId>>,
|
||||
|
||||
/// Rate limiting based on IP addr
|
||||
rate_limits: HashMap<IPAddr, RateLimitBucket>,
|
||||
|
||||
rng: ThreadRng,
|
||||
db: Pool<ConnectionManager<PgConnection>>,
|
||||
}
|
||||
|
||||
impl ChatServer {
|
||||
pub fn startup(db: Pool<ConnectionManager<PgConnection>>) -> ChatServer {
|
||||
// default room
|
||||
let rooms = HashMap::new();
|
||||
|
||||
ChatServer {
|
||||
sessions: HashMap::new(),
|
||||
rate_limits: HashMap::new(),
|
||||
rooms,
|
||||
post_rooms: HashMap::new(),
|
||||
community_rooms: HashMap::new(),
|
||||
user_rooms: HashMap::new(),
|
||||
rng: rand::thread_rng(),
|
||||
db,
|
||||
}
|
||||
}
|
||||
|
||||
/// Send message to all users in the room
|
||||
fn send_room_message(&self, room: i32, message: &str, skip_id: usize) {
|
||||
if let Some(sessions) = self.rooms.get(&room) {
|
||||
fn join_community_room(&mut self, community_id: CommunityId, id: ConnectionId) {
|
||||
// remove session from all rooms
|
||||
for sessions in self.community_rooms.values_mut() {
|
||||
sessions.remove(&id);
|
||||
}
|
||||
|
||||
// If the room doesn't exist yet
|
||||
if self.community_rooms.get_mut(&community_id).is_none() {
|
||||
self.community_rooms.insert(community_id, HashSet::new());
|
||||
}
|
||||
|
||||
self
|
||||
.community_rooms
|
||||
.get_mut(&community_id)
|
||||
.unwrap()
|
||||
.insert(id);
|
||||
}
|
||||
|
||||
fn join_post_room(&mut self, post_id: PostId, id: ConnectionId) {
|
||||
// remove session from all rooms
|
||||
for sessions in self.post_rooms.values_mut() {
|
||||
sessions.remove(&id);
|
||||
}
|
||||
|
||||
// If the room doesn't exist yet
|
||||
if self.post_rooms.get_mut(&post_id).is_none() {
|
||||
self.post_rooms.insert(post_id, HashSet::new());
|
||||
}
|
||||
|
||||
self.post_rooms.get_mut(&post_id).unwrap().insert(id);
|
||||
}
|
||||
|
||||
fn join_user_room(&mut self, user_id: UserId, id: ConnectionId) {
|
||||
// remove session from all rooms
|
||||
for sessions in self.user_rooms.values_mut() {
|
||||
sessions.remove(&id);
|
||||
}
|
||||
|
||||
// If the room doesn't exist yet
|
||||
if self.user_rooms.get_mut(&user_id).is_none() {
|
||||
self.user_rooms.insert(user_id, HashSet::new());
|
||||
}
|
||||
|
||||
self.user_rooms.get_mut(&user_id).unwrap().insert(id);
|
||||
}
|
||||
|
||||
fn send_post_room_message(&self, post_id: PostId, message: &str, skip_id: ConnectionId) {
|
||||
if let Some(sessions) = self.post_rooms.get(&post_id) {
|
||||
for id in sessions {
|
||||
if *id != skip_id {
|
||||
if let Some(info) = self.sessions.get(id) {
|
||||
|
@ -115,43 +165,98 @@ impl ChatServer {
|
|||
}
|
||||
}
|
||||
|
||||
fn join_room(&mut self, room_id: i32, id: usize) {
|
||||
// remove session from all rooms
|
||||
for sessions in self.rooms.values_mut() {
|
||||
sessions.remove(&id);
|
||||
}
|
||||
|
||||
// If the room doesn't exist yet
|
||||
if self.rooms.get_mut(&room_id).is_none() {
|
||||
self.rooms.insert(room_id, HashSet::new());
|
||||
}
|
||||
|
||||
self.rooms.get_mut(&room_id).unwrap().insert(id);
|
||||
}
|
||||
|
||||
fn send_community_message(
|
||||
fn send_community_room_message(
|
||||
&self,
|
||||
community_id: i32,
|
||||
community_id: CommunityId,
|
||||
message: &str,
|
||||
skip_id: usize,
|
||||
) -> Result<(), Error> {
|
||||
use crate::db::post_view::*;
|
||||
use crate::db::*;
|
||||
|
||||
let conn = self.db.get()?;
|
||||
|
||||
let posts = PostQueryBuilder::create(&conn)
|
||||
.listing_type(ListingType::Community)
|
||||
.sort(&SortType::New)
|
||||
.for_community_id(community_id)
|
||||
.limit(9999)
|
||||
.list()?;
|
||||
|
||||
for post in posts {
|
||||
self.send_room_message(post.id, message, skip_id);
|
||||
skip_id: ConnectionId,
|
||||
) {
|
||||
if let Some(sessions) = self.community_rooms.get(&community_id) {
|
||||
for id in sessions {
|
||||
if *id != skip_id {
|
||||
if let Some(info) = self.sessions.get(id) {
|
||||
let _ = info.addr.do_send(WSMessage(message.to_owned()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
fn send_user_room_message(&self, user_id: UserId, message: &str, skip_id: ConnectionId) {
|
||||
if let Some(sessions) = self.user_rooms.get(&user_id) {
|
||||
for id in sessions {
|
||||
if *id != skip_id {
|
||||
if let Some(info) = self.sessions.get(id) {
|
||||
let _ = info.addr.do_send(WSMessage(message.to_owned()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn send_all_message(&self, message: &str, skip_id: ConnectionId) {
|
||||
for id in self.sessions.keys() {
|
||||
if *id != skip_id {
|
||||
if let Some(info) = self.sessions.get(id) {
|
||||
let _ = info.addr.do_send(WSMessage(message.to_owned()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn comment_sends(
|
||||
&self,
|
||||
user_operation: UserOperation,
|
||||
comment: CommentResponse,
|
||||
id: ConnectionId,
|
||||
) -> Result<String, Error> {
|
||||
let mut comment_reply_sent = comment.clone();
|
||||
comment_reply_sent.comment.my_vote = None;
|
||||
comment_reply_sent.comment.user_id = None;
|
||||
|
||||
// For the post room ones, and the directs back to the user
|
||||
// strip out the recipient_ids, so that
|
||||
// users don't get double notifs
|
||||
let mut comment_user_sent = comment.clone();
|
||||
comment_user_sent.recipient_ids = Vec::new();
|
||||
|
||||
let mut comment_post_sent = comment_reply_sent.clone();
|
||||
comment_post_sent.recipient_ids = Vec::new();
|
||||
|
||||
let comment_reply_sent_str = to_json_string(&user_operation, &comment_reply_sent)?;
|
||||
let comment_post_sent_str = to_json_string(&user_operation, &comment_post_sent)?;
|
||||
let comment_user_sent_str = to_json_string(&user_operation, &comment_user_sent)?;
|
||||
|
||||
// Send it to the post room
|
||||
self.send_post_room_message(comment.comment.post_id, &comment_post_sent_str, id);
|
||||
|
||||
// Send it to the recipient(s) including the mentioned users
|
||||
for recipient_id in comment_reply_sent.recipient_ids {
|
||||
self.send_user_room_message(recipient_id, &comment_reply_sent_str, id);
|
||||
}
|
||||
|
||||
Ok(comment_user_sent_str)
|
||||
}
|
||||
|
||||
fn post_sends(
|
||||
&self,
|
||||
user_operation: UserOperation,
|
||||
post: PostResponse,
|
||||
id: ConnectionId,
|
||||
) -> Result<String, Error> {
|
||||
let community_id = post.post.community_id;
|
||||
|
||||
// Don't send my data with it
|
||||
let mut post_sent = post.clone();
|
||||
post_sent.post.my_vote = None;
|
||||
post_sent.post.user_id = None;
|
||||
let post_sent_str = to_json_string(&user_operation, &post_sent)?;
|
||||
|
||||
// Send it to /c/all and that community
|
||||
self.send_community_room_message(0, &post_sent_str, id);
|
||||
self.send_community_room_message(community_id, &post_sent_str, id);
|
||||
|
||||
to_json_string(&user_operation, post)
|
||||
}
|
||||
|
||||
fn check_rate_limit_register(&mut self, id: usize) -> Result<(), Error> {
|
||||
|
@ -233,9 +338,6 @@ impl Handler<Connect> for ChatServer {
|
|||
type Result = usize;
|
||||
|
||||
fn handle(&mut self, msg: Connect, _ctx: &mut Context<Self>) -> Self::Result {
|
||||
// notify all users in same room
|
||||
// self.send_room_message(&"Main".to_owned(), "Someone joined", 0);
|
||||
|
||||
// register session with random id
|
||||
let id = self.rng.gen::<usize>();
|
||||
println!("{} joined", &msg.ip);
|
||||
|
@ -267,15 +369,18 @@ impl Handler<Disconnect> for ChatServer {
|
|||
type Result = ();
|
||||
|
||||
fn handle(&mut self, msg: Disconnect, _: &mut Context<Self>) {
|
||||
// let mut rooms: Vec<i32> = Vec::new();
|
||||
|
||||
// remove address
|
||||
// Remove connections from sessions and all 3 scopes
|
||||
if self.sessions.remove(&msg.id).is_some() {
|
||||
// remove session from all rooms
|
||||
for sessions in self.rooms.values_mut() {
|
||||
if sessions.remove(&msg.id) {
|
||||
// rooms.push(*id);
|
||||
for sessions in self.user_rooms.values_mut() {
|
||||
sessions.remove(&msg.id);
|
||||
}
|
||||
|
||||
for sessions in self.post_rooms.values_mut() {
|
||||
sessions.remove(&msg.id);
|
||||
}
|
||||
|
||||
for sessions in self.community_rooms.values_mut() {
|
||||
sessions.remove(&msg.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -354,10 +459,18 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
|
|||
do_user_operation::<SaveUserSettings, LoginResponse>(user_operation, data, &conn)
|
||||
}
|
||||
UserOperation::AddAdmin => {
|
||||
do_user_operation::<AddAdmin, AddAdminResponse>(user_operation, data, &conn)
|
||||
let add_admin: AddAdmin = serde_json::from_str(data)?;
|
||||
let res = Oper::new(add_admin).perform(&conn)?;
|
||||
let res_str = to_json_string(&user_operation, &res)?;
|
||||
chat.send_all_message(&res_str, msg.id);
|
||||
Ok(res_str)
|
||||
}
|
||||
UserOperation::BanUser => {
|
||||
do_user_operation::<BanUser, BanUserResponse>(user_operation, data, &conn)
|
||||
let ban_user: BanUser = serde_json::from_str(data)?;
|
||||
let res = Oper::new(ban_user).perform(&conn)?;
|
||||
let res_str = to_json_string(&user_operation, &res)?;
|
||||
chat.send_all_message(&res_str, msg.id);
|
||||
Ok(res_str)
|
||||
}
|
||||
UserOperation::GetReplies => {
|
||||
do_user_operation::<GetReplies, GetRepliesResponse>(user_operation, data, &conn)
|
||||
|
@ -372,7 +485,19 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
|
|||
do_user_operation::<MarkAllAsRead, GetRepliesResponse>(user_operation, data, &conn)
|
||||
}
|
||||
UserOperation::GetCommunity => {
|
||||
do_user_operation::<GetCommunity, GetCommunityResponse>(user_operation, data, &conn)
|
||||
let get_community: GetCommunity = serde_json::from_str(data)?;
|
||||
let mut res = Oper::new(get_community).perform(&conn)?;
|
||||
let community_id = res.community.id;
|
||||
|
||||
chat.join_community_room(community_id, msg.id);
|
||||
|
||||
res.online = if let Some(community_users) = chat.community_rooms.get(&community_id) {
|
||||
community_users.len()
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
to_json_string(&user_operation, &res)
|
||||
}
|
||||
UserOperation::ListCommunities => {
|
||||
do_user_operation::<ListCommunities, ListCommunitiesResponse>(user_operation, data, &conn)
|
||||
|
@ -388,7 +513,7 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
|
|||
community_sent.community.user_id = None;
|
||||
community_sent.community.subscribed = None;
|
||||
let community_sent_str = to_json_string(&user_operation, &community_sent)?;
|
||||
chat.send_community_message(community_sent.community.id, &community_sent_str, msg.id)?;
|
||||
chat.send_community_room_message(community_sent.community.id, &community_sent_str, msg.id);
|
||||
to_json_string(&user_operation, &res)
|
||||
}
|
||||
UserOperation::FollowCommunity => {
|
||||
|
@ -403,7 +528,7 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
|
|||
let community_id = ban_from_community.community_id;
|
||||
let res = Oper::new(ban_from_community).perform(&conn)?;
|
||||
let res_str = to_json_string(&user_operation, &res)?;
|
||||
chat.send_community_message(community_id, &res_str, msg.id)?;
|
||||
chat.send_community_room_message(community_id, &res_str, msg.id);
|
||||
Ok(res_str)
|
||||
}
|
||||
UserOperation::AddModToCommunity => {
|
||||
|
@ -411,37 +536,54 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
|
|||
let community_id = mod_add_to_community.community_id;
|
||||
let res = Oper::new(mod_add_to_community).perform(&conn)?;
|
||||
let res_str = to_json_string(&user_operation, &res)?;
|
||||
chat.send_community_message(community_id, &res_str, msg.id)?;
|
||||
chat.send_community_room_message(community_id, &res_str, msg.id);
|
||||
Ok(res_str)
|
||||
}
|
||||
UserOperation::ListCategories => {
|
||||
do_user_operation::<ListCategories, ListCategoriesResponse>(user_operation, data, &conn)
|
||||
}
|
||||
UserOperation::CreatePost => {
|
||||
chat.check_rate_limit_post(msg.id)?;
|
||||
do_user_operation::<CreatePost, PostResponse>(user_operation, data, &conn)
|
||||
}
|
||||
UserOperation::GetPost => {
|
||||
let get_post: GetPost = serde_json::from_str(data)?;
|
||||
chat.join_room(get_post.id, msg.id);
|
||||
let res = Oper::new(get_post).perform(&conn)?;
|
||||
let post_id = get_post.id;
|
||||
chat.join_post_room(post_id, msg.id);
|
||||
let mut res = Oper::new(get_post).perform(&conn)?;
|
||||
|
||||
res.online = if let Some(post_users) = chat.post_rooms.get(&post_id) {
|
||||
post_users.len()
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
to_json_string(&user_operation, &res)
|
||||
}
|
||||
UserOperation::GetPosts => {
|
||||
do_user_operation::<GetPosts, GetPostsResponse>(user_operation, data, &conn)
|
||||
let get_posts: GetPosts = serde_json::from_str(data)?;
|
||||
if get_posts.community_id.is_none() {
|
||||
// 0 is the "all" community
|
||||
chat.join_community_room(0, msg.id);
|
||||
}
|
||||
let res = Oper::new(get_posts).perform(&conn)?;
|
||||
to_json_string(&user_operation, &res)
|
||||
}
|
||||
UserOperation::CreatePost => {
|
||||
chat.check_rate_limit_post(msg.id)?;
|
||||
let create_post: CreatePost = serde_json::from_str(data)?;
|
||||
let res = Oper::new(create_post).perform(&conn)?;
|
||||
|
||||
chat.post_sends(UserOperation::CreatePost, res, msg.id)
|
||||
}
|
||||
UserOperation::CreatePostLike => {
|
||||
chat.check_rate_limit_message(msg.id)?;
|
||||
do_user_operation::<CreatePostLike, CreatePostLikeResponse>(user_operation, data, &conn)
|
||||
let create_post_like: CreatePostLike = serde_json::from_str(data)?;
|
||||
let res = Oper::new(create_post_like).perform(&conn)?;
|
||||
|
||||
chat.post_sends(UserOperation::CreatePostLike, res, msg.id)
|
||||
}
|
||||
UserOperation::EditPost => {
|
||||
let edit_post: EditPost = serde_json::from_str(data)?;
|
||||
let res = Oper::new(edit_post).perform(&conn)?;
|
||||
let mut post_sent = res.clone();
|
||||
post_sent.post.my_vote = None;
|
||||
let post_sent_str = to_json_string(&user_operation, &post_sent)?;
|
||||
chat.send_room_message(post_sent.post.id, &post_sent_str, msg.id);
|
||||
to_json_string(&user_operation, &res)
|
||||
|
||||
chat.post_sends(UserOperation::EditPost, res, msg.id)
|
||||
}
|
||||
UserOperation::SavePost => {
|
||||
do_user_operation::<SavePost, PostResponse>(user_operation, data, &conn)
|
||||
|
@ -449,25 +591,15 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
|
|||
UserOperation::CreateComment => {
|
||||
chat.check_rate_limit_message(msg.id)?;
|
||||
let create_comment: CreateComment = serde_json::from_str(data)?;
|
||||
let post_id = create_comment.post_id;
|
||||
let res = Oper::new(create_comment).perform(&conn)?;
|
||||
let mut comment_sent = res.clone();
|
||||
comment_sent.comment.my_vote = None;
|
||||
comment_sent.comment.user_id = None;
|
||||
let comment_sent_str = to_json_string(&user_operation, &comment_sent)?;
|
||||
chat.send_room_message(post_id, &comment_sent_str, msg.id);
|
||||
to_json_string(&user_operation, &res)
|
||||
|
||||
chat.comment_sends(UserOperation::CreateComment, res, msg.id)
|
||||
}
|
||||
UserOperation::EditComment => {
|
||||
let edit_comment: EditComment = serde_json::from_str(data)?;
|
||||
let post_id = edit_comment.post_id;
|
||||
let res = Oper::new(edit_comment).perform(&conn)?;
|
||||
let mut comment_sent = res.clone();
|
||||
comment_sent.comment.my_vote = None;
|
||||
comment_sent.comment.user_id = None;
|
||||
let comment_sent_str = to_json_string(&user_operation, &comment_sent)?;
|
||||
chat.send_room_message(post_id, &comment_sent_str, msg.id);
|
||||
to_json_string(&user_operation, &res)
|
||||
|
||||
chat.comment_sends(UserOperation::EditComment, res, msg.id)
|
||||
}
|
||||
UserOperation::SaveComment => {
|
||||
do_user_operation::<SaveComment, CommentResponse>(user_operation, data, &conn)
|
||||
|
@ -475,14 +607,9 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
|
|||
UserOperation::CreateCommentLike => {
|
||||
chat.check_rate_limit_message(msg.id)?;
|
||||
let create_comment_like: CreateCommentLike = serde_json::from_str(data)?;
|
||||
let post_id = create_comment_like.post_id;
|
||||
let res = Oper::new(create_comment_like).perform(&conn)?;
|
||||
let mut comment_sent = res.clone();
|
||||
comment_sent.comment.my_vote = None;
|
||||
comment_sent.comment.user_id = None;
|
||||
let comment_sent_str = to_json_string(&user_operation, &comment_sent)?;
|
||||
chat.send_room_message(post_id, &comment_sent_str, msg.id);
|
||||
to_json_string(&user_operation, &res)
|
||||
|
||||
chat.comment_sends(UserOperation::CreateCommentLike, res, msg.id)
|
||||
}
|
||||
UserOperation::GetModlog => {
|
||||
do_user_operation::<GetModlog, GetModlogResponse>(user_operation, data, &conn)
|
||||
|
@ -491,13 +618,16 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
|
|||
do_user_operation::<CreateSite, SiteResponse>(user_operation, data, &conn)
|
||||
}
|
||||
UserOperation::EditSite => {
|
||||
do_user_operation::<EditSite, SiteResponse>(user_operation, data, &conn)
|
||||
let edit_site: EditSite = serde_json::from_str(data)?;
|
||||
let res = Oper::new(edit_site).perform(&conn)?;
|
||||
let res_str = to_json_string(&user_operation, &res)?;
|
||||
chat.send_all_message(&res_str, msg.id);
|
||||
Ok(res_str)
|
||||
}
|
||||
UserOperation::GetSite => {
|
||||
let online: usize = chat.sessions.len();
|
||||
let get_site: GetSite = serde_json::from_str(data)?;
|
||||
let mut res = Oper::new(get_site).perform(&conn)?;
|
||||
res.online = online;
|
||||
res.online = chat.sessions.len();
|
||||
to_json_string(&user_operation, &res)
|
||||
}
|
||||
UserOperation::Search => {
|
||||
|
@ -520,7 +650,13 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
|
|||
}
|
||||
UserOperation::CreatePrivateMessage => {
|
||||
chat.check_rate_limit_message(msg.id)?;
|
||||
do_user_operation::<CreatePrivateMessage, PrivateMessageResponse>(user_operation, data, &conn)
|
||||
let create_private_message: CreatePrivateMessage = serde_json::from_str(data)?;
|
||||
let recipient_id = create_private_message.recipient_id;
|
||||
let res = Oper::new(create_private_message).perform(&conn)?;
|
||||
let res_str = to_json_string(&user_operation, &res)?;
|
||||
|
||||
chat.send_user_room_message(recipient_id, &res_str, msg.id);
|
||||
Ok(res_str)
|
||||
}
|
||||
UserOperation::EditPrivateMessage => {
|
||||
do_user_operation::<EditPrivateMessage, PrivateMessageResponse>(user_operation, data, &conn)
|
||||
|
@ -528,5 +664,11 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
|
|||
UserOperation::GetPrivateMessages => {
|
||||
do_user_operation::<GetPrivateMessages, PrivateMessagesResponse>(user_operation, data, &conn)
|
||||
}
|
||||
UserOperation::UserJoin => {
|
||||
let user_join: UserJoin = serde_json::from_str(data)?;
|
||||
let res = Oper::new(user_join).perform(&conn)?;
|
||||
chat.join_user_room(res.user_id, msg.id);
|
||||
to_json_string(&user_operation, &res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
902
ui/assets/css/themes/_variables.bootstra_386-tmp.scss
vendored
Normal file
902
ui/assets/css/themes/_variables.bootstra_386-tmp.scss
vendored
Normal file
|
@ -0,0 +1,902 @@
|
|||
//
|
||||
// Variables
|
||||
// --------------------------------------------------
|
||||
|
||||
|
||||
//== Colors
|
||||
//
|
||||
//## Gray and brand colors for use across Bootstrap.
|
||||
|
||||
//// colors from bs-2
|
||||
// Grays
|
||||
// -------------------------
|
||||
$black: #000;
|
||||
$grayDark: #555;
|
||||
$gray: #bbb;
|
||||
$grayLight: #bbb;
|
||||
$white: #FFF;
|
||||
|
||||
|
||||
// Accent colors
|
||||
// -------------------------
|
||||
$blue: #5555Ff;
|
||||
$cyan: #55FFFF;
|
||||
$cyanDark: #00AAAA;
|
||||
$blueDark: #000084;
|
||||
$green: #55FF55;
|
||||
$greenDark: #00AA00;
|
||||
$magenta: #FF55FF;
|
||||
$magentaDark: #AA00AA;
|
||||
$red: #FF5555;
|
||||
$redDark: #AA0000;
|
||||
$yellow: #FEFE54;
|
||||
$brown: #AA5500;
|
||||
$orange: #A85400;
|
||||
$pink: #FE54FE;
|
||||
$purple: #FE5454;
|
||||
|
||||
// end colors
|
||||
|
||||
$gray-base: $gray;
|
||||
$gray-darker: $grayDark;
|
||||
$gray-dark: $grayDark;
|
||||
$gray-light: $grayLight;
|
||||
$gray-lighter: $grayLight;
|
||||
|
||||
$brand-primary: $gray;
|
||||
$brand-primary-bg: $cyanDark;
|
||||
$brand-success: $greenDark;
|
||||
$brand-info: $brown;
|
||||
$brand-warning: $magentaDark;
|
||||
$brand-danger: $redDark;
|
||||
|
||||
|
||||
//== Scaffolding
|
||||
//
|
||||
//## Settings for some of the most global styles.
|
||||
|
||||
//** Background color for `<body>`.
|
||||
$body-bg: $blueDark;
|
||||
//** Global text color on `<body>`.
|
||||
$text-color: $gray-light;
|
||||
|
||||
//** Global textual link color.
|
||||
$link-color: $brand-primary;
|
||||
//** Link hover color set via `darken()` function.
|
||||
$link-hover-color: $white;
|
||||
//** Link hover decoration.
|
||||
$link-hover-decoration: none;
|
||||
|
||||
|
||||
//== Typography
|
||||
//
|
||||
//## Font, line-height, and color for body text, headings, and more.
|
||||
|
||||
$font-family-sans-serif: DOS, Monaco, Menlo, Consolas, "Courier New", monospace;
|
||||
$font-family-serif: DOS, Monaco, Menlo, Consolas, "Courier New", monospace;
|
||||
//** Default monospace fonts for `<code>`, `<kbd>`, and `<pre>`.
|
||||
$font-family-monospace: DOS, Monaco, Menlo, Consolas, "Courier New", monospace;
|
||||
$font-family-base: $font-family-sans-serif;
|
||||
|
||||
$baseWidth: 10px;
|
||||
$font-size-base: 18px;
|
||||
$font-size-large: $font-size-base;
|
||||
$font-size-small: $font-size-base;
|
||||
|
||||
$font-size-h1: $font-size-base;
|
||||
$font-size-h2: $font-size-base;
|
||||
$font-size-h3: $font-size-base;
|
||||
$font-size-h4: $font-size-base;
|
||||
$font-size-h5: $font-size-base;
|
||||
$font-size-h6: $font-size-base;
|
||||
|
||||
//** Unit-less `line-height` for use in components like buttons.
|
||||
$baseLineHeight: 19px;
|
||||
$line-height-base: $baseLineHeight;
|
||||
//** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
|
||||
$line-height-computed: $line-height-base;
|
||||
|
||||
//** By default, this inherits from the `<body>`.
|
||||
$headings-font-family: inherit;
|
||||
$headings-font-weight: normal;
|
||||
$headings-line-height: $line-height-base;
|
||||
$headings-color: inherit;
|
||||
|
||||
$space: $baseWidth;
|
||||
$halfbaseLineHeight: ($baseLineHeight / 2);
|
||||
$borderWidth: 2px;
|
||||
$baseLineWidth: ($baseLineHeight / 2);
|
||||
$halfSpace: ($baseWidth / 2);
|
||||
$lhsNB: ($baseWidth / 2 + 1);
|
||||
$rhsNB: ($baseWidth / 2 - 1);
|
||||
$lhs: ($lhsNB - ($borderWidth));
|
||||
$rhs: ($rhsNB - ($borderWidth / 2));
|
||||
$tsNB: ($baseLineHeight / 2);
|
||||
$bsNB: $tsNB;
|
||||
$ts: ($tsNB - ($borderWidth / 2));
|
||||
$bs: $ts;
|
||||
$tsMargin: 3px;
|
||||
|
||||
|
||||
//== Iconography
|
||||
//
|
||||
//## Specify custom location and filename of the included Glyphicons icon font. Useful for those including Bootstrap via Bower.
|
||||
|
||||
//** Load fonts from this directory.
|
||||
$icon-font-path: "../fonts/";
|
||||
//** File name for all font files.
|
||||
$icon-font-name: "glyphicons-halflings-regular";
|
||||
//** Element ID within SVG icon file.
|
||||
$icon-font-svg-id: "glyphicons_halflingsregular";
|
||||
|
||||
|
||||
//== Components
|
||||
//
|
||||
//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
|
||||
|
||||
$padding-base-vertical: 0px;
|
||||
$padding-base-horizontal: 0px;
|
||||
|
||||
$padding-large-vertical: 0px;
|
||||
$padding-large-horizontal: $halfSpace;
|
||||
|
||||
$padding-small-vertical: 0px;
|
||||
$padding-small-horizontal: 0px;
|
||||
|
||||
$padding-xs-vertical: 0px;
|
||||
$padding-xs-horizontal: 0px;
|
||||
|
||||
$line-height-large: $baseLineHeight;
|
||||
$line-height-small: $baseLineHeight;
|
||||
|
||||
$border-radius-base: 0;
|
||||
$border-radius-large: 0;
|
||||
$border-radius-small: 0;
|
||||
|
||||
//** Global color for active items (e.g., navs or dropdowns).
|
||||
$component-active-color: $white;
|
||||
//** Global background color for active items (e.g., navs or dropdowns).
|
||||
$component-active-bg: $black;
|
||||
|
||||
//** Width of the `border` for generating carets that indicator dropdowns.
|
||||
$caret-width-base: 4px;
|
||||
//** Carets increase slightly in size for larger components.
|
||||
$caret-width-large: 5px;
|
||||
|
||||
|
||||
//== Tables
|
||||
//
|
||||
//## Customizes the `.table` component with basic values, each used across all table variations.
|
||||
|
||||
//** Padding for `<th>`s and `<td>`s.
|
||||
$table-cell-padding: $ts $rhs $bs $lhs;
|
||||
//** Padding for cells in `.table-condensed`.
|
||||
$table-condensed-cell-padding: $ts $rhs $bs $lhs;
|
||||
|
||||
//** Default background color used for all tables.
|
||||
$table-bg: transparent;
|
||||
//** Background color used for `.table-striped`.
|
||||
$table-bg-accent: $black;
|
||||
//** Background color used for `.table-hover`.
|
||||
$table-bg-hover: #f5f5f5;
|
||||
$table-bg-active: $table-bg-hover;
|
||||
|
||||
//** Border color for table and cell borders.
|
||||
$table-border-color: $gray;
|
||||
|
||||
|
||||
//== Buttons
|
||||
//
|
||||
//## For each of Bootstrap's buttons, define text, background and border color.
|
||||
|
||||
$btn-font-weight: normal;
|
||||
|
||||
$btn-default-color: $black;
|
||||
$btn-default-bg: $grayLight;
|
||||
$btn-default-border: $grayLight;
|
||||
|
||||
$btn-primary-color: $black;
|
||||
$btn-primary-bg: $cyanDark;
|
||||
$btn-primary-border: $grayLight;
|
||||
|
||||
$btn-success-color: #fff;
|
||||
$btn-success-bg: $brand-success;
|
||||
$btn-success-border: $btn-success-bg;
|
||||
|
||||
$btn-info-color: #fff;
|
||||
$btn-info-bg: $brand-info;
|
||||
$btn-info-border: $btn-info-bg;
|
||||
|
||||
$btn-warning-color: #fff;
|
||||
$btn-warning-bg: $brand-warning;
|
||||
$btn-warning-border: $btn-warning-bg;
|
||||
|
||||
$btn-danger-color: #fff;
|
||||
$btn-danger-bg: $brand-danger;
|
||||
$btn-danger-border: $btn-danger-bg;
|
||||
|
||||
$btn-link-disabled-color: $gray-light;
|
||||
|
||||
|
||||
//== Forms
|
||||
//
|
||||
//##
|
||||
|
||||
//** `<input>` background color
|
||||
$input-bg: $cyanDark;
|
||||
//** `<input disabled>` background color
|
||||
$input-bg-disabled: $gray-lighter;
|
||||
|
||||
//** Text color for `<input>`s
|
||||
$input-color: $white;
|
||||
//** `<input>` border color
|
||||
$input-border: #ccc;
|
||||
|
||||
// TODO: Rename `$input-border-radius` to `$input-border-radius-base` in v4
|
||||
//** Default `.form-control` border radius
|
||||
// This has no effect on `<select>`s in some browsers, due to the limited stylability of `<select>`s in CSS.
|
||||
$input-border-radius: $border-radius-base;
|
||||
//** Large `.form-control` border radius
|
||||
$input-border-radius-large: $border-radius-large;
|
||||
//** Small `.form-control` border radius
|
||||
$input-border-radius-small: $border-radius-small;
|
||||
|
||||
//** Border color for inputs on focus
|
||||
$input-border-focus: $black;
|
||||
|
||||
//** Placeholder text color
|
||||
$input-color-placeholder: $black;
|
||||
|
||||
//** Default `.form-control` height
|
||||
$input-height-base: $line-height-computed;
|
||||
//** Large `.form-control` height
|
||||
$input-height-large: $input-height-base;
|
||||
//** Small `.form-control` height
|
||||
$input-height-small: $input-height-base;
|
||||
|
||||
$legend-color: $gray-dark;
|
||||
$legend-border-color: #e5e5e5;
|
||||
|
||||
//** Background color for textual input addons
|
||||
$input-group-addon-bg: $gray-lighter;
|
||||
//** Border color for textual input addons
|
||||
$input-group-addon-border-color: $input-border;
|
||||
|
||||
//** Disabled cursor for form controls and buttons.
|
||||
$cursor-disabled: not-allowed;
|
||||
|
||||
|
||||
//== Dropdowns
|
||||
//
|
||||
//## Dropdown menu container and contents.
|
||||
|
||||
//** Background for the dropdown menu.
|
||||
$dropdown-bg: $gray;
|
||||
//** Dropdown menu `border-color`.
|
||||
$dropdown-border: rgb(0,0,0);
|
||||
//** Dropdown menu `border-color` **for IE8**.
|
||||
$dropdown-fallback-border: #ccc;
|
||||
//** Divider color for between dropdown items.
|
||||
$dropdown-divider-bg: $black;
|
||||
|
||||
//** Dropdown link text color.
|
||||
$dropdown-link-color: $black;
|
||||
//** Hover color for dropdown links.
|
||||
$dropdown-link-hover-color: $gray;
|
||||
//** Hover background for dropdown links.
|
||||
$dropdown-link-hover-bg: $black;
|
||||
|
||||
//** Active dropdown menu item text color.
|
||||
$dropdown-link-active-color: $component-active-color;
|
||||
//** Active dropdown menu item background color.
|
||||
$dropdown-link-active-bg: $component-active-bg;
|
||||
|
||||
//** Disabled dropdown menu item background color.
|
||||
$dropdown-link-disabled-color: $gray-light;
|
||||
|
||||
//** Text color for headers within dropdown menus.
|
||||
$dropdown-header-color: $black;
|
||||
|
||||
//** Deprecated `$dropdown-caret-color` as of v3.1.0
|
||||
$dropdown-caret-color: #000;
|
||||
|
||||
|
||||
//-- Z-index master list
|
||||
//
|
||||
// Warning: Avoid customizing these values. They're used for a bird's eye view
|
||||
// of components dependent on the z-axis and are designed to all work together.
|
||||
//
|
||||
// Note: These variables are not generated into the Customizer.
|
||||
|
||||
$zindex-navbar: 1000;
|
||||
$zindex-dropdown: 1000;
|
||||
$zindex-popover: 1060;
|
||||
$zindex-tooltip: 1070;
|
||||
$zindex-navbar-fixed: 1030;
|
||||
$zindex-modal: 1040;
|
||||
|
||||
|
||||
//== Media queries breakpoints
|
||||
//
|
||||
//## Define the breakpoints at which your layout will change, adapting to different screen sizes.
|
||||
|
||||
// Extra small screen / phone
|
||||
//** Deprecated `$screen-xs` as of v3.0.1
|
||||
$screen-xs: 480px;
|
||||
//** Deprecated `$screen-xs-min` as of v3.2.0
|
||||
$screen-xs-min: $screen-xs;
|
||||
//** Deprecated `$screen-phone` as of v3.0.1
|
||||
$screen-phone: $screen-xs-min;
|
||||
|
||||
// Small screen / tablet
|
||||
//** Deprecated `$screen-sm` as of v3.0.1
|
||||
$screen-sm: 768px;
|
||||
$screen-sm-min: $screen-sm;
|
||||
//** Deprecated `$screen-tablet` as of v3.0.1
|
||||
$screen-tablet: $screen-sm-min;
|
||||
|
||||
// Medium screen / desktop
|
||||
//** Deprecated `$screen-md` as of v3.0.1
|
||||
$screen-md: 992px;
|
||||
$screen-md-min: $screen-md;
|
||||
//** Deprecated `$screen-desktop` as of v3.0.1
|
||||
$screen-desktop: $screen-md-min;
|
||||
|
||||
// Large screen / wide desktop
|
||||
//** Deprecated `$screen-lg` as of v3.0.1
|
||||
$screen-lg: 1200px;
|
||||
$screen-lg-min: $screen-lg;
|
||||
//** Deprecated `$screen-lg-desktop` as of v3.0.1
|
||||
$screen-lg-desktop: $screen-lg-min;
|
||||
|
||||
// So media queries don't overlap when required, provide a maximum
|
||||
$screen-xs-max: ($screen-sm-min - 1);
|
||||
$screen-sm-max: ($screen-md-min - 1);
|
||||
$screen-md-max: ($screen-lg-min - 1);
|
||||
|
||||
|
||||
//== Grid system
|
||||
//
|
||||
//## Define your custom responsive grid.
|
||||
|
||||
//** Number of columns in the grid.
|
||||
$grid-columns: 12;
|
||||
//** Padding between columns. Gets divided in half for the left and right.
|
||||
$grid-gutter-width: ($baseWidth * 2);
|
||||
// Navbar collapse
|
||||
//** Point at which the navbar becomes uncollapsed.
|
||||
$grid-float-breakpoint: $screen-sm-min;
|
||||
//** Point at which the navbar begins collapsing.
|
||||
$grid-float-breakpoint-max: ($grid-float-breakpoint);
|
||||
|
||||
|
||||
//== Container sizes
|
||||
//
|
||||
//## Define the maximum width of `.container` for different screen sizes.
|
||||
|
||||
// Small screen / tablet
|
||||
$container-tablet: (720px + $grid-gutter-width);
|
||||
//** For `$screen-sm-min` and up.
|
||||
$container-sm: $container-tablet;
|
||||
|
||||
// Medium screen / desktop
|
||||
$container-desktop: (940px + $grid-gutter-width);
|
||||
//** For `$screen-md-min` and up.
|
||||
$container-md: $container-desktop;
|
||||
|
||||
// Large screen / wide desktop
|
||||
$container-large-desktop: (1140px + $grid-gutter-width);
|
||||
//** For `$screen-lg-min` and up.
|
||||
$container-lg: $container-large-desktop;
|
||||
|
||||
|
||||
//== Navbar
|
||||
//
|
||||
//##
|
||||
|
||||
// Basics of a navbar
|
||||
$navbar-height: 0px;
|
||||
$navbar-margin-bottom: $line-height-computed;
|
||||
$navbar-border-radius: $border-radius-base;
|
||||
$navbar-padding-horizontal: ($baseWidth * 2);
|
||||
$navbar-padding-vertical: 0;
|
||||
$navbar-collapse-max-height: 340px;
|
||||
|
||||
$navbar-default-color: $black;
|
||||
$navbar-default-bg: $grayLight;
|
||||
$navbar-default-border: $navbar-default-bg;
|
||||
|
||||
// Navbar links
|
||||
$navbar-default-link-color: $black;
|
||||
$navbar-default-link-hover-color: $white;
|
||||
$navbar-default-link-hover-bg: $black;
|
||||
$navbar-default-link-active-color: $white;
|
||||
$navbar-default-link-active-bg: $black;
|
||||
$navbar-default-link-disabled-color: $gray;
|
||||
$navbar-default-link-disabled-bg: transparent;
|
||||
|
||||
// Navbar brand label
|
||||
$navbar-default-brand-color: $navbar-default-link-color;
|
||||
$navbar-default-brand-hover-color: $navbar-default-brand-color;
|
||||
$navbar-default-brand-hover-bg: transparent;
|
||||
|
||||
// Navbar toggle
|
||||
$navbar-default-toggle-hover-bg: #ddd;
|
||||
$navbar-default-toggle-icon-bar-bg: #888;
|
||||
$navbar-default-toggle-border-color: #ddd;
|
||||
|
||||
|
||||
// Inverted navbar
|
||||
// Reset inverted navbar basics
|
||||
$navbar-inverse-color: $gray;
|
||||
$navbar-inverse-bg: $black;
|
||||
$navbar-inverse-border: $navbar-inverse-bg;
|
||||
|
||||
// Inverted navbar links
|
||||
$navbar-inverse-link-color: $gray-light;
|
||||
$navbar-inverse-link-hover-color: $black;
|
||||
$navbar-inverse-link-hover-bg: $grayLight;
|
||||
$navbar-inverse-link-active-color: $white;
|
||||
$navbar-inverse-link-active-bg: $grayDark;
|
||||
$navbar-inverse-link-disabled-color: $gray;
|
||||
$navbar-inverse-link-disabled-bg: transparent;
|
||||
|
||||
// Inverted navbar brand label
|
||||
$navbar-inverse-brand-color: $navbar-inverse-link-color;
|
||||
$navbar-inverse-brand-hover-color: #fff;
|
||||
$navbar-inverse-brand-hover-bg: transparent;
|
||||
|
||||
// Inverted navbar toggle
|
||||
$navbar-inverse-toggle-hover-bg: $grayLight;
|
||||
$navbar-inverse-toggle-icon-bar-bg: #fff;
|
||||
$navbar-inverse-toggle-border-color: #333;
|
||||
|
||||
|
||||
//== Navs
|
||||
//
|
||||
//##
|
||||
|
||||
//=== Shared nav styles
|
||||
$nav-link-padding: 0 $baseWidth;
|
||||
$nav-link-hover-bg: $gray-lighter;
|
||||
|
||||
$nav-disabled-link-color: $gray-light;
|
||||
$nav-disabled-link-hover-color: $gray-light;
|
||||
|
||||
//== Tabs
|
||||
$nav-tabs-border-color: #ddd;
|
||||
|
||||
$nav-tabs-link-hover-border-color: $gray-lighter;
|
||||
|
||||
$nav-tabs-active-link-hover-bg: $black;
|
||||
$nav-tabs-active-link-hover-color: $white;
|
||||
|
||||
$nav-tabs-justified-active-link-border-color: $body-bg;
|
||||
|
||||
//== Pills
|
||||
$nav-pills-border-radius: $border-radius-base;
|
||||
$nav-pills-active-link-hover-bg: $component-active-bg;
|
||||
$nav-pills-active-link-hover-color: $component-active-color;
|
||||
|
||||
|
||||
//== Pagination
|
||||
//
|
||||
//##
|
||||
|
||||
$pagination-color: $black;
|
||||
$pagination-bg: $gray;
|
||||
$pagination-border: #ddd;
|
||||
|
||||
$pagination-hover-color: $link-hover-color;
|
||||
$pagination-hover-bg: $gray-lighter;
|
||||
$pagination-hover-border: #ddd;
|
||||
|
||||
$pagination-active-color: #fff;
|
||||
$pagination-active-bg: $brand-primary;
|
||||
$pagination-active-border: $brand-primary;
|
||||
|
||||
$pagination-disabled-color: $gray-light;
|
||||
$pagination-disabled-bg: #fff;
|
||||
$pagination-disabled-border: #ddd;
|
||||
|
||||
|
||||
//== Pager
|
||||
//
|
||||
//##
|
||||
|
||||
$pager-bg: $pagination-bg;
|
||||
$pager-border: $pagination-border;
|
||||
$pager-border-radius: 0;
|
||||
|
||||
$pager-hover-bg: $pagination-hover-bg;
|
||||
|
||||
$pager-active-bg: $pagination-active-bg;
|
||||
$pager-active-color: $pagination-active-color;
|
||||
|
||||
$pager-disabled-color: $pagination-disabled-color;
|
||||
|
||||
|
||||
//== Jumbotron
|
||||
//
|
||||
//##
|
||||
|
||||
$jumbotron-padding: ($ts) ($rhs + $baseWidth) ($bs) ($lhs + $baseWidth);
|
||||
$jumbotron-color: $white;
|
||||
$jumbotron-bg: transparent;
|
||||
$jumbotron-heading-color: inherit;
|
||||
$jumbotron-font-size: $font-size-base;
|
||||
|
||||
|
||||
//== Form states and alerts
|
||||
//
|
||||
//## Define colors for form feedback states and, by default, alerts.
|
||||
|
||||
$state-success-text: $green;
|
||||
$state-success-bg: $greenDark;
|
||||
$state-success-border: $state-success-bg;
|
||||
|
||||
$state-info-text: $yellow;
|
||||
$state-info-bg: $brown;
|
||||
$state-info-border: $state-info-bg;
|
||||
|
||||
$state-warning-text: $magenta;
|
||||
$state-warning-bg: $magentaDark;
|
||||
$state-warning-border: $state-warning-bg;
|
||||
|
||||
$state-danger-text: $red;
|
||||
$state-danger-bg: $black;
|
||||
$state-danger-border: $state-danger-bg;
|
||||
|
||||
|
||||
//== Tooltips
|
||||
//
|
||||
//##
|
||||
|
||||
//** Tooltip max width
|
||||
$tooltip-max-width: ($baseWidth * 25);
|
||||
//** Tooltip text color
|
||||
$tooltip-color: $white;
|
||||
//** Tooltip background color
|
||||
$tooltip-bg: $grayDark;
|
||||
$tooltip-opacity: 1;
|
||||
|
||||
//** Tooltip arrow width
|
||||
$tooltip-arrow-width: 0px;
|
||||
//** Tooltip arrow color
|
||||
$tooltip-arrow-color: $tooltip-bg;
|
||||
|
||||
|
||||
//== Popovers
|
||||
//
|
||||
//##
|
||||
|
||||
//** Popover body background color
|
||||
$popover-bg: $gray;
|
||||
//** Popover maximum width
|
||||
$popover-max-width: ($baseWidth * 20);
|
||||
//** Popover border color
|
||||
$popover-border-color: rgb(0,0,0);
|
||||
//** Popover fallback border color
|
||||
$popover-fallback-border-color: #ccc;
|
||||
|
||||
//** Popover title background color
|
||||
$popover-title-bg: $greenDark;
|
||||
|
||||
//** Popover arrow width
|
||||
$popover-arrow-width: 10px;
|
||||
//** Popover arrow color
|
||||
$popover-arrow-color: $popover-bg;
|
||||
|
||||
//** Popover outer arrow width
|
||||
$popover-arrow-outer-width: ($popover-arrow-width + 1);
|
||||
//** Popover outer arrow color
|
||||
$popover-arrow-outer-color: $popover-border-color;
|
||||
//** Popover outer arrow fallback color
|
||||
$popover-arrow-outer-fallback-color: $popover-fallback-border-color;
|
||||
|
||||
|
||||
//== Labels
|
||||
//
|
||||
//##
|
||||
|
||||
//** Default label background color
|
||||
$label-default-bg: $gray-light;
|
||||
//** Primary label background color
|
||||
$label-primary-bg: $brand-primary-bg;
|
||||
//** Success label background color
|
||||
$label-success-bg: $brand-success;
|
||||
//** Info label background color
|
||||
$label-info-bg: $brand-info;
|
||||
//** Warning label background color
|
||||
$label-warning-bg: $brand-warning;
|
||||
//** Danger label background color
|
||||
$label-danger-bg: $brand-danger;
|
||||
|
||||
//** Default label text color
|
||||
$label-color: #fff;
|
||||
//** Default text color of a linked label
|
||||
$label-link-hover-color: #fff;
|
||||
|
||||
|
||||
//== Modals
|
||||
//
|
||||
//##
|
||||
|
||||
//** Padding applied to the modal body
|
||||
$modal-inner-padding: 0 $baseWidth;
|
||||
|
||||
//** Padding applied to the modal title
|
||||
$modal-title-padding: 0 $baseWidth;
|
||||
//** Modal title line-height
|
||||
$modal-title-line-height: $line-height-base;
|
||||
|
||||
//** Background color of modal content area
|
||||
$modal-content-bg: $gray;
|
||||
//** Modal content border color
|
||||
$modal-content-border-color: rgb(0,0,0);
|
||||
//** Modal content border color **for IE8**
|
||||
$modal-content-fallback-border-color: #999;
|
||||
|
||||
//** Modal backdrop background color
|
||||
$modal-backdrop-bg: #000;
|
||||
//** Modal backdrop opacity
|
||||
// $modal-backdrop-opacity: @include 5;
|
||||
//** Modal header border color
|
||||
$modal-header-border-color: #e5e5e5;
|
||||
//** Modal footer border color
|
||||
$modal-footer-border-color: $modal-header-border-color;
|
||||
|
||||
$modal-lg: 900px;
|
||||
$modal-md: 600px;
|
||||
$modal-sm: 300px;
|
||||
|
||||
|
||||
//== Alerts
|
||||
//
|
||||
//## Define alert colors, border radius, and padding.
|
||||
|
||||
$alert-padding: $line-height-base ($baseWidth * 2);
|
||||
$alert-border-radius: $border-radius-base;
|
||||
$alert-link-font-weight: normal;
|
||||
|
||||
$alert-success-bg: $state-success-bg;
|
||||
$alert-success-text: $state-success-text;
|
||||
$alert-success-border: $state-success-border;
|
||||
|
||||
$alert-info-bg: $state-info-bg;
|
||||
$alert-info-text: $state-info-text;
|
||||
$alert-info-border: $state-info-border;
|
||||
|
||||
$alert-warning-bg: $state-warning-bg;
|
||||
$alert-warning-text: $state-warning-text;
|
||||
$alert-warning-border: $state-warning-border;
|
||||
|
||||
$alert-danger-bg: $state-danger-bg;
|
||||
$alert-danger-text: $state-danger-text;
|
||||
$alert-danger-border: $state-danger-border;
|
||||
|
||||
|
||||
//== Progress bars
|
||||
//
|
||||
//##
|
||||
|
||||
//** Background color of the whole progress component
|
||||
$progress-bg: $black;
|
||||
//** Progress bar text color
|
||||
$progress-bar-color: $black;
|
||||
//** Variable for setting rounded corners on progress bar.
|
||||
$progress-border-radius: $border-radius-base;
|
||||
|
||||
//** Default progress bar color
|
||||
$progress-bar-bg: $brand-primary;
|
||||
//** Success progress bar color
|
||||
$progress-bar-success-bg: $brand-success;
|
||||
//** Warning progress bar color
|
||||
$progress-bar-warning-bg: $brand-warning;
|
||||
//** Danger progress bar color
|
||||
$progress-bar-danger-bg: $brand-danger;
|
||||
//** Info progress bar color
|
||||
$progress-bar-info-bg: $brand-info;
|
||||
|
||||
|
||||
//== List group
|
||||
//
|
||||
//##
|
||||
|
||||
//** Background color on `.list-group-item`
|
||||
$list-group-bg: $gray;
|
||||
//** `.list-group-item` border color
|
||||
$list-group-border: #ddd;
|
||||
//** List group border radius
|
||||
$list-group-border-radius: $border-radius-base;
|
||||
|
||||
//** Background color of single list items on hover
|
||||
$list-group-hover-bg: $black;
|
||||
//** Text color of active list items
|
||||
$list-group-active-color: $component-active-color;
|
||||
//** Background color of active list items
|
||||
$list-group-active-bg: $component-active-bg;
|
||||
//** Border color of active list elements
|
||||
$list-group-active-border: $list-group-active-bg;
|
||||
//** Text color for content within active list items
|
||||
$list-group-active-text-color: $component-active-color;
|
||||
|
||||
//** Text color of disabled list items
|
||||
$list-group-disabled-color: $gray-dark;
|
||||
//** Background color of disabled list items
|
||||
$list-group-disabled-bg: $gray-lighter;
|
||||
//** Text color for content within disabled list items
|
||||
$list-group-disabled-text-color: $list-group-disabled-color;
|
||||
|
||||
$list-group-link-color: $black;
|
||||
$list-group-link-hover-color: $list-group-link-color;
|
||||
$list-group-link-heading-color: #333;
|
||||
|
||||
|
||||
//== Panels
|
||||
//
|
||||
//##
|
||||
|
||||
$panel-bg: $gray;
|
||||
$panel-body-padding: 0 $rhsNB 0 $lhsNB;
|
||||
$panel-heading-padding: 0 $rhsNB 0 $lhsNB;
|
||||
$panel-footer-padding: $panel-heading-padding;
|
||||
$panel-border-radius: $border-radius-base;
|
||||
|
||||
//** Border color for elements within panels
|
||||
$panel-inner-border: #ddd;
|
||||
$panel-footer-bg: #f5f5f5;
|
||||
|
||||
$panel-default-text: $white;
|
||||
$panel-default-border: #ddd;
|
||||
$panel-default-heading-bg: $grayDark;
|
||||
|
||||
$panel-primary-text: $white;
|
||||
$panel-primary-border: $brand-primary;
|
||||
$panel-primary-heading-bg: $cyanDark;
|
||||
|
||||
$panel-success-text: $state-success-text;
|
||||
$panel-success-border: $state-success-border;
|
||||
$panel-success-heading-bg: $state-success-bg;
|
||||
|
||||
$panel-info-text: $state-info-text;
|
||||
$panel-info-border: $state-info-border;
|
||||
$panel-info-heading-bg: $state-info-bg;
|
||||
|
||||
$panel-warning-text: $state-warning-text;
|
||||
$panel-warning-border: $state-warning-border;
|
||||
$panel-warning-heading-bg: $state-warning-bg;
|
||||
|
||||
$panel-danger-text: $state-danger-text;
|
||||
$panel-danger-border: $state-danger-border;
|
||||
$panel-danger-heading-bg: $state-danger-bg;
|
||||
|
||||
|
||||
//== Thumbnails
|
||||
//
|
||||
//##
|
||||
|
||||
//** Padding around the thumbnail image
|
||||
$thumbnail-padding: 4px;
|
||||
//** Thumbnail background color
|
||||
$thumbnail-bg: $body-bg;
|
||||
//** Thumbnail border color
|
||||
$thumbnail-border: #ddd;
|
||||
//** Thumbnail border radius
|
||||
$thumbnail-border-radius: $border-radius-base;
|
||||
|
||||
//** Custom text color for thumbnail captions
|
||||
$thumbnail-caption-color: $text-color;
|
||||
//** Padding around the thumbnail caption
|
||||
$thumbnail-caption-padding: 9px;
|
||||
|
||||
|
||||
//== Wells
|
||||
//
|
||||
//##
|
||||
|
||||
$well-bg: $greenDark;
|
||||
$well-border: $well-bg;
|
||||
|
||||
|
||||
//== Badges
|
||||
//
|
||||
//##
|
||||
|
||||
$badge-color: $black;
|
||||
//** Linked badge text color on hover
|
||||
$badge-link-hover-color: #fff;
|
||||
$badge-bg: $gray-light;
|
||||
|
||||
//** Badge text color in active nav link
|
||||
$badge-active-color: $link-color;
|
||||
//** Badge background color in active nav link
|
||||
$badge-active-bg: $black;
|
||||
|
||||
$badge-font-weight: normal;
|
||||
$badge-line-height: $line-height-base;
|
||||
$badge-border-radius: 0;
|
||||
|
||||
|
||||
//== Breadcrumbs
|
||||
//
|
||||
//##
|
||||
|
||||
$breadcrumb-padding-vertical: 8px;
|
||||
$breadcrumb-padding-horizontal: 15px;
|
||||
//** Breadcrumb background color
|
||||
$breadcrumb-bg: #f5f5f5;
|
||||
//** Breadcrumb text color
|
||||
$breadcrumb-color: #ccc;
|
||||
//** Text color of current page in the breadcrumb
|
||||
$breadcrumb-active-color: $gray-light;
|
||||
//** Textual separator for between breadcrumb elements
|
||||
$breadcrumb-separator: "/";
|
||||
|
||||
|
||||
//== Carousel
|
||||
//
|
||||
//##
|
||||
|
||||
$carousel-text-shadow: none;
|
||||
|
||||
$carousel-control-color: #fff;
|
||||
$carousel-control-width: 15%;
|
||||
$carousel-control-opacity: 1;
|
||||
$carousel-control-font-size: $font-size-base;
|
||||
|
||||
$carousel-indicator-active-bg: #fff;
|
||||
$carousel-indicator-border-color: #fff;
|
||||
|
||||
$carousel-caption-color: #fff;
|
||||
|
||||
|
||||
//== Close
|
||||
//
|
||||
//##
|
||||
|
||||
$close-font-weight: normal;
|
||||
$close-color: #000;
|
||||
$close-text-shadow: none;
|
||||
|
||||
|
||||
//== Code
|
||||
//
|
||||
//##
|
||||
|
||||
$code-color: #c7254e;
|
||||
$code-bg: #f9f2f4;
|
||||
|
||||
$kbd-color: #fff;
|
||||
$kbd-bg: #333;
|
||||
|
||||
$pre-bg: #f5f5f5;
|
||||
$pre-color: $gray-dark;
|
||||
$pre-border-color: #ccc;
|
||||
$pre-scrollable-max-height: 340px;
|
||||
|
||||
|
||||
//== Type
|
||||
//
|
||||
//##
|
||||
|
||||
//** Horizontal offset for forms and lists.
|
||||
$component-offset-horizontal: 180px;
|
||||
//** Text muted color
|
||||
$text-muted: $gray-dark;
|
||||
//** Abbreviations and acronyms border color
|
||||
$abbr-border-color: $gray-light;
|
||||
//** Headings small color
|
||||
$headings-small-color: $gray-light;
|
||||
//** Blockquote small color
|
||||
$blockquote-small-color: $gray-light;
|
||||
//** Blockquote font size
|
||||
$blockquote-font-size: $font-size-base;
|
||||
//** Blockquote border color
|
||||
$blockquote-border-color: $gray-lighter;
|
||||
//** Page header border color
|
||||
$page-header-border-color: $gray-lighter;
|
||||
//** Width of horizontal description list titles
|
||||
$dl-horizontal-offset: $component-offset-horizontal;
|
||||
//** Horizontal line color.
|
||||
$hr-border: $black;
|
40
ui/assets/css/themes/_variables.i386.scss
vendored
Normal file
40
ui/assets/css/themes/_variables.i386.scss
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
|
||||
$blue: #5555Ff;
|
||||
$cyan: #55FFFF;
|
||||
$green: #55FF55;
|
||||
$indigo: #FF55FF;
|
||||
$red: #FF5555;
|
||||
$yellow: #FEFE54;
|
||||
$orange: #A85400;
|
||||
$pink: #FE54FE;
|
||||
$purple: #FE5454;
|
||||
$primary: #FEFE54;
|
||||
$body-bg: #000084;
|
||||
$gray-300: #bbb;
|
||||
$body-color: $gray-300;
|
||||
$link-hover-color: $white;
|
||||
$font-family-sans-serif: DOS, Monaco, Menlo, Consolas, "Courier New", monospace;
|
||||
$font-family-monospace: DOS, Monaco, Menlo, Consolas, "Courier New", monospace;
|
||||
$navbar-dark-color: $gray-300;
|
||||
$navbar-light-brand-color: $gray-300;
|
||||
$success: #00AA00;
|
||||
$danger: #AA0000;
|
||||
$info: #00AAAA;
|
||||
$warning: #AA00AA;
|
||||
$navbar-dark-active-color: $gray-100;
|
||||
$enable-rounded: false;
|
||||
$input-color: $white;
|
||||
$input-bg: rgb(102, 102, 102);
|
||||
$input-disabled-bg: $gray-800;
|
||||
$nav-tabs-link-active-color: $gray-100;
|
||||
$navbar-dark-hover-color: rgba($gray-300, .75);
|
||||
$light: $gray-800;
|
||||
$navbar-light-disabled-color: $gray-800;
|
||||
$navbar-light-active-color: $gray-100;
|
||||
$navbar-light-hover-color: $gray-200;
|
||||
$navbar-light-color: $gray-300;
|
||||
$card-bg: $gray-800;
|
||||
$card-border-color: $white;
|
||||
$input-placeholder-color: $gray-500;
|
||||
$mark-bg: #463b00;
|
||||
$secondary: $gray-900;
|
1
ui/assets/css/themes/i386.min.css
vendored
Normal file
1
ui/assets/css/themes/i386.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
12
ui/assets/css/themes/materia.min.css
vendored
Normal file
12
ui/assets/css/themes/materia.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
3
ui/package.json
vendored
3
ui/package.json
vendored
|
@ -17,7 +17,7 @@
|
|||
"@types/jwt-decode": "^2.2.1",
|
||||
"@types/markdown-it": "^0.0.9",
|
||||
"@types/markdown-it-container": "^2.0.2",
|
||||
"@types/node": "^13.5.0",
|
||||
"@types/node": "^13.7.0",
|
||||
"autosize": "^4.0.2",
|
||||
"bootswatch": "^4.3.1",
|
||||
"classcat": "^1.1.3",
|
||||
|
@ -35,6 +35,7 @@
|
|||
"markdown-it-emoji": "^1.4.0",
|
||||
"moment": "^2.24.0",
|
||||
"prettier": "^1.18.2",
|
||||
"reconnecting-websocket": "^4.3.0",
|
||||
"rxjs": "^6.4.0",
|
||||
"terser": "^4.6.3",
|
||||
"toastify-js": "^1.6.2",
|
||||
|
|
9
ui/src/components/comment-form.tsx
vendored
9
ui/src/components/comment-form.tsx
vendored
|
@ -15,7 +15,6 @@ import { WebSocketService, UserService } from '../services';
|
|||
import autosize from 'autosize';
|
||||
import Tribute from 'tributejs/src/Tribute.js';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
interface CommentFormProps {
|
||||
postId?: number;
|
||||
|
@ -127,7 +126,7 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
|
|||
.previewMode && 'active'}`}
|
||||
onClick={linkEvent(this, this.handlePreviewToggle)}
|
||||
>
|
||||
<T i18nKey="preview">#</T>
|
||||
{i18n.t('preview')}
|
||||
</button>
|
||||
)}
|
||||
{this.props.node && (
|
||||
|
@ -136,7 +135,7 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
|
|||
class="btn btn-sm btn-secondary mr-2"
|
||||
onClick={linkEvent(this, this.handleReplyCancel)}
|
||||
>
|
||||
<T i18nKey="cancel">#</T>
|
||||
{i18n.t('cancel')}
|
||||
</button>
|
||||
)}
|
||||
<a
|
||||
|
@ -144,14 +143,14 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
|
|||
target="_blank"
|
||||
class="d-inline-block float-right text-muted small font-weight-bold"
|
||||
>
|
||||
<T i18nKey="formatting_help">#</T>
|
||||
{i18n.t('formatting_help')}
|
||||
</a>
|
||||
<form class="d-inline-block mr-2 float-right text-muted small font-weight-bold">
|
||||
<label
|
||||
htmlFor={`file-upload-${this.id}`}
|
||||
className={`${UserService.Instance.user && 'pointer'}`}
|
||||
>
|
||||
<T i18nKey="upload_image">#</T>
|
||||
{i18n.t('upload_image')}
|
||||
</label>
|
||||
<input
|
||||
id={`file-upload-${this.id}`}
|
||||
|
|
67
ui/src/components/comment-node.tsx
vendored
67
ui/src/components/comment-node.tsx
vendored
|
@ -30,7 +30,6 @@ import { MomentTime } from './moment-time';
|
|||
import { CommentForm } from './comment-form';
|
||||
import { CommentNodes } from './comment-nodes';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
interface CommentNodeState {
|
||||
showReply: boolean;
|
||||
|
@ -117,7 +116,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
.viewOnly && 'no-click'}`}
|
||||
>
|
||||
<button
|
||||
className={`btn p-0 ${
|
||||
className={`btn btn-link p-0 ${
|
||||
node.comment.my_vote == 1 ? 'text-info' : 'text-muted'
|
||||
}`}
|
||||
onClick={linkEvent(node, this.handleCommentUpvote)}
|
||||
|
@ -137,7 +136,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
</div>
|
||||
{WebSocketService.Instance.site.enable_downvotes && (
|
||||
<button
|
||||
className={`btn p-0 ${
|
||||
className={`btn btn-link p-0 ${
|
||||
node.comment.my_vote == -1 ? 'text-danger' : 'text-muted'
|
||||
}`}
|
||||
onClick={linkEvent(node, this.handleCommentDownvote)}
|
||||
|
@ -180,22 +179,22 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
</li>
|
||||
{this.isMod && (
|
||||
<li className="list-inline-item badge badge-light">
|
||||
<T i18nKey="mod">#</T>
|
||||
{i18n.t('mod')}
|
||||
</li>
|
||||
)}
|
||||
{this.isAdmin && (
|
||||
<li className="list-inline-item badge badge-light">
|
||||
<T i18nKey="admin">#</T>
|
||||
{i18n.t('admin')}
|
||||
</li>
|
||||
)}
|
||||
{this.isPostCreator && (
|
||||
<li className="list-inline-item badge badge-light">
|
||||
<T i18nKey="creator">#</T>
|
||||
{i18n.t('creator')}
|
||||
</li>
|
||||
)}
|
||||
{(node.comment.banned_from_community || node.comment.banned) && (
|
||||
<li className="list-inline-item badge badge-danger">
|
||||
<T i18nKey="banned">#</T>
|
||||
{i18n.t('banned')}
|
||||
</li>
|
||||
)}
|
||||
<li className="list-inline-item">
|
||||
|
@ -258,7 +257,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
class="pointer"
|
||||
onClick={linkEvent(this, this.handleReplyClick)}
|
||||
>
|
||||
<T i18nKey="reply">#</T>
|
||||
{i18n.t('reply')}
|
||||
</span>
|
||||
</li>
|
||||
<li className="list-inline-item mr-2">
|
||||
|
@ -276,7 +275,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
class="pointer"
|
||||
onClick={linkEvent(this, this.handleEditClick)}
|
||||
>
|
||||
<T i18nKey="edit">#</T>
|
||||
{i18n.t('edit')}
|
||||
</span>
|
||||
</li>
|
||||
<li className="list-inline-item">
|
||||
|
@ -307,7 +306,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
className="pointer"
|
||||
onClick={linkEvent(this, this.handleViewSource)}
|
||||
>
|
||||
<T i18nKey="view_source">#</T>
|
||||
{i18n.t('view_source')}
|
||||
</span>
|
||||
</li>
|
||||
<li className="list-inline-item">
|
||||
|
@ -315,7 +314,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
className="text-muted"
|
||||
to={`/post/${node.comment.post_id}/comment/${node.comment.id}`}
|
||||
>
|
||||
<T i18nKey="link">#</T>
|
||||
{i18n.t('link')}
|
||||
</Link>
|
||||
</li>
|
||||
{/* Admins and mods can remove comments */}
|
||||
|
@ -331,7 +330,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
this.handleModRemoveShow
|
||||
)}
|
||||
>
|
||||
<T i18nKey="remove">#</T>
|
||||
{i18n.t('remove')}
|
||||
</span>
|
||||
) : (
|
||||
<span
|
||||
|
@ -341,7 +340,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
this.handleModRemoveSubmit
|
||||
)}
|
||||
>
|
||||
<T i18nKey="restore">#</T>
|
||||
{i18n.t('restore')}
|
||||
</span>
|
||||
)}
|
||||
</li>
|
||||
|
@ -360,7 +359,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
this.handleModBanFromCommunityShow
|
||||
)}
|
||||
>
|
||||
<T i18nKey="ban">#</T>
|
||||
{i18n.t('ban')}
|
||||
</span>
|
||||
) : (
|
||||
<span
|
||||
|
@ -370,7 +369,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
this.handleModBanFromCommunitySubmit
|
||||
)}
|
||||
>
|
||||
<T i18nKey="unban">#</T>
|
||||
{i18n.t('unban')}
|
||||
</span>
|
||||
)}
|
||||
</li>
|
||||
|
@ -392,7 +391,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
) : (
|
||||
<>
|
||||
<span class="d-inline-block mr-1">
|
||||
<T i18nKey="are_you_sure">#</T>
|
||||
{i18n.t('are_you_sure')}
|
||||
</span>
|
||||
<span
|
||||
class="pointer d-inline-block mr-1"
|
||||
|
@ -401,7 +400,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
this.handleAddModToCommunity
|
||||
)}
|
||||
>
|
||||
<T i18nKey="yes">#</T>
|
||||
{i18n.t('yes')}
|
||||
</span>
|
||||
<span
|
||||
class="pointer d-inline-block"
|
||||
|
@ -410,7 +409,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
this.handleCancelConfirmAppointAsMod
|
||||
)}
|
||||
>
|
||||
<T i18nKey="no">#</T>
|
||||
{i18n.t('no')}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
|
@ -429,12 +428,12 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
this.handleShowConfirmTransferCommunity
|
||||
)}
|
||||
>
|
||||
<T i18nKey="transfer_community">#</T>
|
||||
{i18n.t('transfer_community')}
|
||||
</span>
|
||||
) : (
|
||||
<>
|
||||
<span class="d-inline-block mr-1">
|
||||
<T i18nKey="are_you_sure">#</T>
|
||||
{i18n.t('are_you_sure')}
|
||||
</span>
|
||||
<span
|
||||
class="pointer d-inline-block mr-1"
|
||||
|
@ -443,7 +442,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
this.handleTransferCommunity
|
||||
)}
|
||||
>
|
||||
<T i18nKey="yes">#</T>
|
||||
{i18n.t('yes')}
|
||||
</span>
|
||||
<span
|
||||
class="pointer d-inline-block"
|
||||
|
@ -452,7 +451,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
this.handleCancelShowConfirmTransferCommunity
|
||||
)}
|
||||
>
|
||||
<T i18nKey="no">#</T>
|
||||
{i18n.t('no')}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
|
@ -468,7 +467,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
class="pointer"
|
||||
onClick={linkEvent(this, this.handleModBanShow)}
|
||||
>
|
||||
<T i18nKey="ban_from_site">#</T>
|
||||
{i18n.t('ban_from_site')}
|
||||
</span>
|
||||
) : (
|
||||
<span
|
||||
|
@ -478,7 +477,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
this.handleModBanSubmit
|
||||
)}
|
||||
>
|
||||
<T i18nKey="unban_from_site">#</T>
|
||||
{i18n.t('unban_from_site')}
|
||||
</span>
|
||||
)}
|
||||
</li>
|
||||
|
@ -500,13 +499,13 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
) : (
|
||||
<>
|
||||
<span class="d-inline-block mr-1">
|
||||
<T i18nKey="are_you_sure">#</T>
|
||||
{i18n.t('are_you_sure')}
|
||||
</span>
|
||||
<span
|
||||
class="pointer d-inline-block mr-1"
|
||||
onClick={linkEvent(this, this.handleAddAdmin)}
|
||||
>
|
||||
<T i18nKey="yes">#</T>
|
||||
{i18n.t('yes')}
|
||||
</span>
|
||||
<span
|
||||
class="pointer d-inline-block"
|
||||
|
@ -515,7 +514,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
this.handleCancelConfirmAppointAsAdmin
|
||||
)}
|
||||
>
|
||||
<T i18nKey="no">#</T>
|
||||
{i18n.t('no')}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
|
@ -534,18 +533,18 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
this.handleShowConfirmTransferSite
|
||||
)}
|
||||
>
|
||||
<T i18nKey="transfer_site">#</T>
|
||||
{i18n.t('transfer_site')}
|
||||
</span>
|
||||
) : (
|
||||
<>
|
||||
<span class="d-inline-block mr-1">
|
||||
<T i18nKey="are_you_sure">#</T>
|
||||
{i18n.t('are_you_sure')}
|
||||
</span>
|
||||
<span
|
||||
class="pointer d-inline-block mr-1"
|
||||
onClick={linkEvent(this, this.handleTransferSite)}
|
||||
>
|
||||
<T i18nKey="yes">#</T>
|
||||
{i18n.t('yes')}
|
||||
</span>
|
||||
<span
|
||||
class="pointer d-inline-block"
|
||||
|
@ -554,7 +553,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
this.handleCancelShowConfirmTransferSite
|
||||
)}
|
||||
>
|
||||
<T i18nKey="no">#</T>
|
||||
{i18n.t('no')}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
|
@ -579,16 +578,14 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
onInput={linkEvent(this, this.handleModRemoveReasonChange)}
|
||||
/>
|
||||
<button type="submit" class="btn btn-secondary">
|
||||
<T i18nKey="remove_comment">#</T>
|
||||
{i18n.t('remove_comment')}
|
||||
</button>
|
||||
</form>
|
||||
)}
|
||||
{this.state.showBanDialog && (
|
||||
<form onSubmit={linkEvent(this, this.handleModBanBothSubmit)}>
|
||||
<div class="form-group row">
|
||||
<label class="col-form-label">
|
||||
<T i18nKey="reason">#</T>
|
||||
</label>
|
||||
<label class="col-form-label">{i18n.t('reason')}</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control mr-2"
|
||||
|
|
36
ui/src/components/communities.tsx
vendored
36
ui/src/components/communities.tsx
vendored
|
@ -15,7 +15,6 @@ import {
|
|||
import { WebSocketService } from '../services';
|
||||
import { wsJsonToRes, toast } from '../utils';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
declare const Sortable: any;
|
||||
|
||||
|
@ -83,30 +82,20 @@ export class Communities extends Component<any, CommunitiesState> {
|
|||
</h5>
|
||||
) : (
|
||||
<div>
|
||||
<h5>
|
||||
<T i18nKey="list_of_communities">#</T>
|
||||
</h5>
|
||||
<h5>{i18n.t('list_of_communities')}</h5>
|
||||
<div class="table-responsive">
|
||||
<table id="community_table" class="table table-sm table-hover">
|
||||
<thead class="pointer">
|
||||
<tr>
|
||||
<th>
|
||||
<T i18nKey="name">#</T>
|
||||
</th>
|
||||
<th class="d-none d-lg-table-cell">
|
||||
<T i18nKey="title">#</T>
|
||||
</th>
|
||||
<th>
|
||||
<T i18nKey="category">#</T>
|
||||
</th>
|
||||
<th class="text-right">
|
||||
<T i18nKey="subscribers">#</T>
|
||||
<th>{i18n.t('name')}</th>
|
||||
<th class="d-none d-lg-table-cell">{i18n.t('title')}</th>
|
||||
<th>{i18n.t('category')}</th>
|
||||
<th class="text-right">{i18n.t('subscribers')}</th>
|
||||
<th class="text-right d-none d-lg-table-cell">
|
||||
{i18n.t('posts')}
|
||||
</th>
|
||||
<th class="text-right d-none d-lg-table-cell">
|
||||
<T i18nKey="posts">#</T>
|
||||
</th>
|
||||
<th class="text-right d-none d-lg-table-cell">
|
||||
<T i18nKey="comments">#</T>
|
||||
{i18n.t('comments')}
|
||||
</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
|
@ -139,7 +128,7 @@ export class Communities extends Component<any, CommunitiesState> {
|
|||
this.handleUnsubscribe
|
||||
)}
|
||||
>
|
||||
<T i18nKey="unsubscribe">#</T>
|
||||
{i18n.t('unsubscribe')}
|
||||
</span>
|
||||
) : (
|
||||
<span
|
||||
|
@ -149,7 +138,7 @@ export class Communities extends Component<any, CommunitiesState> {
|
|||
this.handleSubscribe
|
||||
)}
|
||||
>
|
||||
<T i18nKey="subscribe">#</T>
|
||||
{i18n.t('subscribe')}
|
||||
</span>
|
||||
)}
|
||||
</td>
|
||||
|
@ -173,15 +162,16 @@ export class Communities extends Component<any, CommunitiesState> {
|
|||
class="btn btn-sm btn-secondary mr-1"
|
||||
onClick={linkEvent(this, this.prevPage)}
|
||||
>
|
||||
<T i18nKey="prev">#</T>
|
||||
{i18n.t('prev')}
|
||||
</button>
|
||||
)}
|
||||
|
||||
{this.state.communities.length == communityLimit && (
|
||||
<button
|
||||
class="btn btn-sm btn-secondary"
|
||||
onClick={linkEvent(this, this.nextPage)}
|
||||
>
|
||||
<T i18nKey="next">#</T>
|
||||
{i18n.t('next')}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
|
28
ui/src/components/community-form.tsx
vendored
28
ui/src/components/community-form.tsx
vendored
|
@ -21,7 +21,6 @@ import {
|
|||
import Tribute from 'tributejs/src/Tribute.js';
|
||||
import autosize from 'autosize';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
import { Community } from '../interfaces';
|
||||
|
||||
|
@ -108,12 +107,13 @@ export class CommunityForm extends Component<
|
|||
return (
|
||||
<form onSubmit={linkEvent(this, this.handleCreateCommunitySubmit)}>
|
||||
<div class="form-group row">
|
||||
<label class="col-12 col-form-label">
|
||||
<T i18nKey="name">#</T>
|
||||
<label class="col-12 col-form-label" htmlFor="community-name">
|
||||
{i18n.t('name')}
|
||||
</label>
|
||||
<div class="col-12">
|
||||
<input
|
||||
type="text"
|
||||
id="community-name"
|
||||
class="form-control"
|
||||
value={this.state.communityForm.name}
|
||||
onInput={linkEvent(this, this.handleCommunityNameChange)}
|
||||
|
@ -125,13 +125,15 @@ export class CommunityForm extends Component<
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-12 col-form-label">
|
||||
<T i18nKey="title">#</T>
|
||||
<label class="col-12 col-form-label" htmlFor="community-title">
|
||||
{i18n.t('title')}
|
||||
</label>
|
||||
<div class="col-12">
|
||||
<input
|
||||
type="text"
|
||||
id="community-title"
|
||||
value={this.state.communityForm.title}
|
||||
onInput={linkEvent(this, this.handleCommunityTitleChange)}
|
||||
class="form-control"
|
||||
|
@ -142,8 +144,8 @@ export class CommunityForm extends Component<
|
|||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-12 col-form-label">
|
||||
<T i18nKey="sidebar">#</T>
|
||||
<label class="col-12 col-form-label" htmlFor={this.id}>
|
||||
{i18n.t('sidebar')}
|
||||
</label>
|
||||
<div class="col-12">
|
||||
<textarea
|
||||
|
@ -157,12 +159,13 @@ export class CommunityForm extends Component<
|
|||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-12 col-form-label">
|
||||
<T i18nKey="category">#</T>
|
||||
<label class="col-12 col-form-label" htmlFor="community-category">
|
||||
{i18n.t('category')}
|
||||
</label>
|
||||
<div class="col-12">
|
||||
<select
|
||||
class="form-control"
|
||||
id="community-category"
|
||||
value={this.state.communityForm.category_id}
|
||||
onInput={linkEvent(this, this.handleCommunityCategoryChange)}
|
||||
>
|
||||
|
@ -179,12 +182,13 @@ export class CommunityForm extends Component<
|
|||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
id="community-nsfw"
|
||||
type="checkbox"
|
||||
checked={this.state.communityForm.nsfw}
|
||||
onChange={linkEvent(this, this.handleCommunityNsfwChange)}
|
||||
/>
|
||||
<label class="form-check-label">
|
||||
<T i18nKey="nsfw">#</T>
|
||||
<label class="form-check-label" htmlFor="community-nsfw">
|
||||
{i18n.t('nsfw')}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -209,7 +213,7 @@ export class CommunityForm extends Component<
|
|||
class="btn btn-secondary"
|
||||
onClick={linkEvent(this, this.handleCancel)}
|
||||
>
|
||||
<T i18nKey="cancel">#</T>
|
||||
{i18n.t('cancel')}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
|
71
ui/src/components/community.tsx
vendored
71
ui/src/components/community.tsx
vendored
|
@ -14,21 +14,16 @@ import {
|
|||
GetCommunityForm,
|
||||
ListingType,
|
||||
GetPostsResponse,
|
||||
CreatePostLikeResponse,
|
||||
PostResponse,
|
||||
AddModToCommunityResponse,
|
||||
BanFromCommunityResponse,
|
||||
WebSocketJsonResponse,
|
||||
} from '../interfaces';
|
||||
import { WebSocketService, UserService } from '../services';
|
||||
import { PostListings } from './post-listings';
|
||||
import { SortSelect } from './sort-select';
|
||||
import { Sidebar } from './sidebar';
|
||||
import {
|
||||
wsJsonToRes,
|
||||
routeSortTypeToEnum,
|
||||
fetchLimit,
|
||||
postRefetchSeconds,
|
||||
toast,
|
||||
} from '../utils';
|
||||
import { T } from 'inferno-i18next';
|
||||
import { wsJsonToRes, routeSortTypeToEnum, fetchLimit, toast } from '../utils';
|
||||
import { i18n } from '../i18next';
|
||||
|
||||
interface State {
|
||||
|
@ -37,6 +32,7 @@ interface State {
|
|||
communityName: string;
|
||||
moderators: Array<CommunityUser>;
|
||||
admins: Array<UserView>;
|
||||
online: number;
|
||||
loading: boolean;
|
||||
posts: Array<Post>;
|
||||
sort: SortType;
|
||||
|
@ -45,7 +41,6 @@ interface State {
|
|||
|
||||
export class Community extends Component<any, State> {
|
||||
private subscription: Subscription;
|
||||
private postFetcher: any;
|
||||
private emptyState: State = {
|
||||
community: {
|
||||
id: null,
|
||||
|
@ -67,6 +62,7 @@ export class Community extends Component<any, State> {
|
|||
admins: [],
|
||||
communityId: Number(this.props.match.params.id),
|
||||
communityName: this.props.match.params.name,
|
||||
online: null,
|
||||
loading: true,
|
||||
posts: [],
|
||||
sort: this.getSortTypeFromProps(this.props),
|
||||
|
@ -108,7 +104,6 @@ export class Community extends Component<any, State> {
|
|||
|
||||
componentWillUnmount() {
|
||||
this.subscription.unsubscribe();
|
||||
clearInterval(this.postFetcher);
|
||||
}
|
||||
|
||||
// Necessary for back button for some reason
|
||||
|
@ -140,12 +135,12 @@ export class Community extends Component<any, State> {
|
|||
{this.state.community.title}
|
||||
{this.state.community.removed && (
|
||||
<small className="ml-2 text-muted font-italic">
|
||||
<T i18nKey="removed">#</T>
|
||||
{i18n.t('removed')}
|
||||
</small>
|
||||
)}
|
||||
{this.state.community.nsfw && (
|
||||
<small className="ml-2 text-muted font-italic">
|
||||
<T i18nKey="nsfw">#</T>
|
||||
{i18n.t('nsfw')}
|
||||
</small>
|
||||
)}
|
||||
</h5>
|
||||
|
@ -158,6 +153,7 @@ export class Community extends Component<any, State> {
|
|||
community={this.state.community}
|
||||
moderators={this.state.moderators}
|
||||
admins={this.state.admins}
|
||||
online={this.state.online}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -192,7 +188,7 @@ export class Community extends Component<any, State> {
|
|||
class="btn btn-sm btn-secondary mr-1"
|
||||
onClick={linkEvent(this, this.prevPage)}
|
||||
>
|
||||
<T i18nKey="prev">#</T>
|
||||
{i18n.t('prev')}
|
||||
</button>
|
||||
)}
|
||||
{this.state.posts.length == fetchLimit && (
|
||||
|
@ -200,7 +196,7 @@ export class Community extends Component<any, State> {
|
|||
class="btn btn-sm btn-secondary"
|
||||
onClick={linkEvent(this, this.nextPage)}
|
||||
>
|
||||
<T i18nKey="next">#</T>
|
||||
{i18n.t('next')}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
@ -240,11 +236,6 @@ export class Community extends Component<any, State> {
|
|||
);
|
||||
}
|
||||
|
||||
keepFetchingPosts() {
|
||||
this.fetchPosts();
|
||||
this.postFetcher = setInterval(() => this.fetchPosts(), postRefetchSeconds);
|
||||
}
|
||||
|
||||
fetchPosts() {
|
||||
let getPostsForm: GetPostsForm = {
|
||||
page: this.state.page,
|
||||
|
@ -268,9 +259,10 @@ export class Community extends Component<any, State> {
|
|||
this.state.community = data.community;
|
||||
this.state.moderators = data.moderators;
|
||||
this.state.admins = data.admins;
|
||||
this.state.online = data.online;
|
||||
document.title = `/c/${this.state.community.name} - ${WebSocketService.Instance.site.name}`;
|
||||
this.setState(this.state);
|
||||
this.keepFetchingPosts();
|
||||
this.fetchPosts();
|
||||
} else if (res.op == UserOperation.EditCommunity) {
|
||||
let data = res.data as CommunityResponse;
|
||||
this.state.community = data.community;
|
||||
|
@ -286,13 +278,44 @@ export class Community extends Component<any, State> {
|
|||
this.state.posts = data.posts;
|
||||
this.state.loading = false;
|
||||
this.setState(this.state);
|
||||
} else if (res.op == UserOperation.CreatePostLike) {
|
||||
let data = res.data as CreatePostLikeResponse;
|
||||
} else if (res.op == UserOperation.EditPost) {
|
||||
let data = res.data as PostResponse;
|
||||
let found = this.state.posts.find(c => c.id == data.post.id);
|
||||
found.my_vote = data.post.my_vote;
|
||||
|
||||
found.url = data.post.url;
|
||||
found.name = data.post.name;
|
||||
found.nsfw = data.post.nsfw;
|
||||
|
||||
this.setState(this.state);
|
||||
} else if (res.op == UserOperation.CreatePost) {
|
||||
let data = res.data as PostResponse;
|
||||
this.state.posts.unshift(data.post);
|
||||
this.setState(this.state);
|
||||
} else if (res.op == UserOperation.CreatePostLike) {
|
||||
let data = res.data as PostResponse;
|
||||
let found = this.state.posts.find(c => c.id == data.post.id);
|
||||
|
||||
found.score = data.post.score;
|
||||
found.upvotes = data.post.upvotes;
|
||||
found.downvotes = data.post.downvotes;
|
||||
if (data.post.my_vote !== null) {
|
||||
found.my_vote = data.post.my_vote;
|
||||
found.upvoteLoading = false;
|
||||
found.downvoteLoading = false;
|
||||
}
|
||||
|
||||
this.setState(this.state);
|
||||
} else if (res.op == UserOperation.AddModToCommunity) {
|
||||
let data = res.data as AddModToCommunityResponse;
|
||||
this.state.moderators = data.moderators;
|
||||
this.setState(this.state);
|
||||
} else if (res.op == UserOperation.BanFromCommunity) {
|
||||
let data = res.data as BanFromCommunityResponse;
|
||||
|
||||
this.state.posts
|
||||
.filter(p => p.creator_id == data.user.id)
|
||||
.forEach(p => (p.banned = data.banned));
|
||||
|
||||
this.setState(this.state);
|
||||
}
|
||||
}
|
||||
|
|
5
ui/src/components/create-community.tsx
vendored
5
ui/src/components/create-community.tsx
vendored
|
@ -3,7 +3,6 @@ import { CommunityForm } from './community-form';
|
|||
import { Community } from '../interfaces';
|
||||
import { WebSocketService } from '../services';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
export class CreateCommunity extends Component<any, any> {
|
||||
constructor(props: any, context: any) {
|
||||
|
@ -22,9 +21,7 @@ export class CreateCommunity extends Component<any, any> {
|
|||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-6 offset-lg-3 mb-4">
|
||||
<h5>
|
||||
<T i18nKey="create_community">#</T>
|
||||
</h5>
|
||||
<h5>{i18n.t('create_community')}</h5>
|
||||
<CommunityForm onCreate={this.handleCommunityCreate} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
5
ui/src/components/create-post.tsx
vendored
5
ui/src/components/create-post.tsx
vendored
|
@ -3,7 +3,6 @@ import { PostForm } from './post-form';
|
|||
import { WebSocketService } from '../services';
|
||||
import { PostFormParams } from '../interfaces';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
export class CreatePost extends Component<any, any> {
|
||||
constructor(props: any, context: any) {
|
||||
|
@ -22,9 +21,7 @@ export class CreatePost extends Component<any, any> {
|
|||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-6 offset-lg-3 mb-4">
|
||||
<h5>
|
||||
<T i18nKey="create_post">#</T>
|
||||
</h5>
|
||||
<h5>{i18n.t('create_post')}</h5>
|
||||
<PostForm onCreate={this.handlePostCreate} params={this.params} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
10
ui/src/components/footer.tsx
vendored
10
ui/src/components/footer.tsx
vendored
|
@ -2,7 +2,7 @@ import { Component } from 'inferno';
|
|||
import { Link } from 'inferno-router';
|
||||
import { repoUrl } from '../utils';
|
||||
import { version } from '../version';
|
||||
import { T } from 'inferno-i18next';
|
||||
import { i18n } from '../i18next';
|
||||
|
||||
export class Footer extends Component<any, any> {
|
||||
constructor(props: any, context: any) {
|
||||
|
@ -19,22 +19,22 @@ export class Footer extends Component<any, any> {
|
|||
</li>
|
||||
<li class="nav-item">
|
||||
<Link class="nav-link" to="/modlog">
|
||||
<T i18nKey="modlog">#</T>
|
||||
{i18n.t('modlog')}
|
||||
</Link>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href={'/docs/index.html'}>
|
||||
<T i18nKey="docs">#</T>
|
||||
{i18n.t('docs')}
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<Link class="nav-link" to="/sponsors">
|
||||
<T i18nKey="donate">#</T>
|
||||
{i18n.t('donate')}
|
||||
</Link>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href={repoUrl}>
|
||||
<T i18nKey="code">#</T>
|
||||
{i18n.t('code')}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
59
ui/src/components/inbox.tsx
vendored
59
ui/src/components/inbox.tsx
vendored
|
@ -122,7 +122,7 @@ export class Inbox extends Component<any, InboxState> {
|
|||
<ul class="list-inline mb-1 text-muted small font-weight-bold">
|
||||
<li className="list-inline-item">
|
||||
<span class="pointer" onClick={this.markAllAsRead}>
|
||||
<T i18nKey="mark_all_as_read">#</T>
|
||||
{i18n.t('mark_all_as_read')}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -147,36 +147,20 @@ export class Inbox extends Component<any, InboxState> {
|
|||
onChange={linkEvent(this, this.handleUnreadOrAllChange)}
|
||||
class="custom-select custom-select-sm w-auto mr-2"
|
||||
>
|
||||
<option disabled>
|
||||
<T i18nKey="type">#</T>
|
||||
</option>
|
||||
<option value={UnreadOrAll.Unread}>
|
||||
<T i18nKey="unread">#</T>
|
||||
</option>
|
||||
<option value={UnreadOrAll.All}>
|
||||
<T i18nKey="all">#</T>
|
||||
</option>
|
||||
<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>
|
||||
<T i18nKey="type">#</T>
|
||||
</option>
|
||||
<option value={UnreadType.All}>
|
||||
<T i18nKey="all">#</T>
|
||||
</option>
|
||||
<option value={UnreadType.Replies}>
|
||||
<T i18nKey="replies">#</T>
|
||||
</option>
|
||||
<option value={UnreadType.Mentions}>
|
||||
<T i18nKey="mentions">#</T>
|
||||
</option>
|
||||
<option value={UnreadType.Messages}>
|
||||
<T i18nKey="messages">#</T>
|
||||
</option>
|
||||
<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
|
||||
sort={this.state.sort}
|
||||
|
@ -248,14 +232,14 @@ export class Inbox extends Component<any, InboxState> {
|
|||
class="btn btn-sm btn-secondary mr-1"
|
||||
onClick={linkEvent(this, this.prevPage)}
|
||||
>
|
||||
<T i18nKey="prev">#</T>
|
||||
{i18n.t('prev')}
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
class="btn btn-sm btn-secondary"
|
||||
onClick={linkEvent(this, this.nextPage)}
|
||||
>
|
||||
<T i18nKey="next">#</T>
|
||||
{i18n.t('next')}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
@ -421,10 +405,25 @@ export class Inbox extends Component<any, InboxState> {
|
|||
this.sendUnreadCount();
|
||||
this.setState(this.state);
|
||||
} else if (res.op == UserOperation.CreateComment) {
|
||||
// let res: CommentResponse = msg;
|
||||
let data = res.data as CommentResponse;
|
||||
|
||||
if (data.recipient_ids.includes(UserService.Instance.user.id)) {
|
||||
this.state.replies.unshift(data.comment);
|
||||
this.setState(this.state);
|
||||
} else if (data.comment.creator_id == UserService.Instance.user.id) {
|
||||
toast(i18n.t('reply_sent'));
|
||||
// this.state.replies.unshift(res.comment); // TODO do this right
|
||||
// this.setState(this.state);
|
||||
}
|
||||
this.setState(this.state);
|
||||
} else if (res.op == UserOperation.CreatePrivateMessage) {
|
||||
let data = res.data as PrivateMessageResponse;
|
||||
|
||||
if (data.message.recipient_id == UserService.Instance.user.id) {
|
||||
this.state.messages.unshift(data.message);
|
||||
this.setState(this.state);
|
||||
} else if (data.message.creator_id == UserService.Instance.user.id) {
|
||||
toast(i18n.t('message_sent'));
|
||||
}
|
||||
this.setState(this.state);
|
||||
} else if (res.op == UserOperation.SaveComment) {
|
||||
let data = res.data as CommentResponse;
|
||||
let found = this.state.replies.find(c => c.id == data.comment.id);
|
||||
|
|
57
ui/src/components/login.tsx
vendored
57
ui/src/components/login.tsx
vendored
|
@ -13,7 +13,6 @@ import {
|
|||
import { WebSocketService, UserService } from '../services';
|
||||
import { wsJsonToRes, validEmail, toast } from '../utils';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
interface State {
|
||||
loginForm: LoginForm;
|
||||
|
@ -78,15 +77,19 @@ export class Login extends Component<any, State> {
|
|||
return (
|
||||
<div>
|
||||
<form onSubmit={linkEvent(this, this.handleLoginSubmit)}>
|
||||
<h5>Login</h5>
|
||||
<h5>{i18n.t('login')}</h5>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">
|
||||
<T i18nKey="email_or_username">#</T>
|
||||
<label
|
||||
class="col-sm-2 col-form-label"
|
||||
htmlFor="login-email-or-username"
|
||||
>
|
||||
{i18n.t('email_or_username')}
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="login-email-or-username"
|
||||
value={this.state.loginForm.username_or_email}
|
||||
onInput={linkEvent(this, this.handleLoginUsernameChange)}
|
||||
required
|
||||
|
@ -95,12 +98,13 @@ export class Login extends Component<any, State> {
|
|||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">
|
||||
<T i18nKey="password">#</T>
|
||||
<label class="col-sm-2 col-form-label" htmlFor="login-password">
|
||||
{i18n.t('password')}
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="password"
|
||||
id="login-password"
|
||||
value={this.state.loginForm.password}
|
||||
onInput={linkEvent(this, this.handleLoginPasswordChange)}
|
||||
class="form-control"
|
||||
|
@ -111,7 +115,7 @@ export class Login extends Component<any, State> {
|
|||
onClick={linkEvent(this, this.handlePasswordReset)}
|
||||
className="btn p-0 btn-link d-inline-block float-right text-muted small font-weight-bold"
|
||||
>
|
||||
<T i18nKey="forgot_password">#</T>
|
||||
{i18n.t('forgot_password')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -135,16 +139,17 @@ export class Login extends Component<any, State> {
|
|||
registerForm() {
|
||||
return (
|
||||
<form onSubmit={linkEvent(this, this.handleRegisterSubmit)}>
|
||||
<h5>
|
||||
<T i18nKey="sign_up">#</T>
|
||||
</h5>
|
||||
<h5>{i18n.t('sign_up')}</h5>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">
|
||||
<T i18nKey="username">#</T>
|
||||
<label class="col-sm-2 col-form-label" htmlFor="register-username">
|
||||
{i18n.t('username')}
|
||||
</label>
|
||||
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="text"
|
||||
id="register-username"
|
||||
class="form-control"
|
||||
value={this.state.registerForm.username}
|
||||
onInput={linkEvent(this, this.handleRegisterUsernameChange)}
|
||||
|
@ -155,13 +160,15 @@ export class Login extends Component<any, State> {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">
|
||||
<T i18nKey="email">#</T>
|
||||
<label class="col-sm-2 col-form-label" htmlFor="register-email">
|
||||
{i18n.t('email')}
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="email"
|
||||
id="register-email"
|
||||
class="form-control"
|
||||
placeholder={i18n.t('optional')}
|
||||
value={this.state.registerForm.email}
|
||||
|
@ -170,13 +177,15 @@ export class Login extends Component<any, State> {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">
|
||||
<T i18nKey="password">#</T>
|
||||
<label class="col-sm-2 col-form-label" htmlFor="register-password">
|
||||
{i18n.t('password')}
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="password"
|
||||
id="register-password"
|
||||
value={this.state.registerForm.password}
|
||||
onInput={linkEvent(this, this.handleRegisterPasswordChange)}
|
||||
class="form-control"
|
||||
|
@ -184,13 +193,18 @@ export class Login extends Component<any, State> {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">
|
||||
<T i18nKey="verify_password">#</T>
|
||||
<label
|
||||
class="col-sm-2 col-form-label"
|
||||
htmlFor="register-verify-password"
|
||||
>
|
||||
{i18n.t('verify_password')}
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="password"
|
||||
id="register-verify-password"
|
||||
value={this.state.registerForm.password_verify}
|
||||
onInput={linkEvent(this, this.handleRegisterPasswordVerifyChange)}
|
||||
class="form-control"
|
||||
|
@ -204,12 +218,13 @@ export class Login extends Component<any, State> {
|
|||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
id="register-show-nsfw"
|
||||
type="checkbox"
|
||||
checked={this.state.registerForm.show_nsfw}
|
||||
onChange={linkEvent(this, this.handleRegisterShowNsfwChange)}
|
||||
/>
|
||||
<label class="form-check-label">
|
||||
<T i18nKey="show_nsfw">#</T>
|
||||
<label class="form-check-label" htmlFor="register-show-nsfw">
|
||||
{i18n.t('show_nsfw')}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -306,6 +321,7 @@ export class Login extends Component<any, State> {
|
|||
this.state = this.emptyState;
|
||||
this.setState(this.state);
|
||||
UserService.Instance.login(data);
|
||||
WebSocketService.Instance.userJoin();
|
||||
toast(i18n.t('logged_in'));
|
||||
this.props.history.push('/');
|
||||
} else if (res.op == UserOperation.Register) {
|
||||
|
@ -313,6 +329,7 @@ export class Login extends Component<any, State> {
|
|||
this.state = this.emptyState;
|
||||
this.setState(this.state);
|
||||
UserService.Instance.login(data);
|
||||
WebSocketService.Instance.userJoin();
|
||||
this.props.history.push('/communities');
|
||||
} else if (res.op == UserOperation.PasswordReset) {
|
||||
toast(i18n.t('reset_password_mail_sent'));
|
||||
|
|
180
ui/src/components/main.tsx
vendored
180
ui/src/components/main.tsx
vendored
|
@ -14,9 +14,11 @@ import {
|
|||
ListingType,
|
||||
SiteResponse,
|
||||
GetPostsResponse,
|
||||
CreatePostLikeResponse,
|
||||
PostResponse,
|
||||
Post,
|
||||
GetPostsForm,
|
||||
AddAdminResponse,
|
||||
BanUserResponse,
|
||||
WebSocketJsonResponse,
|
||||
} from '../interfaces';
|
||||
import { WebSocketService, UserService } from '../services';
|
||||
|
@ -31,7 +33,6 @@ import {
|
|||
fetchLimit,
|
||||
routeSortTypeToEnum,
|
||||
routeListingTypeToEnum,
|
||||
postRefetchSeconds,
|
||||
pictshareAvatarThumbnail,
|
||||
showAvatars,
|
||||
toast,
|
||||
|
@ -42,7 +43,7 @@ import { T } from 'inferno-i18next';
|
|||
interface MainState {
|
||||
subscribedCommunities: Array<CommunityUser>;
|
||||
trendingCommunities: Array<Community>;
|
||||
site: GetSiteResponse;
|
||||
siteRes: GetSiteResponse;
|
||||
showEditSite: boolean;
|
||||
loading: boolean;
|
||||
posts: Array<Post>;
|
||||
|
@ -53,11 +54,10 @@ interface MainState {
|
|||
|
||||
export class Main extends Component<any, MainState> {
|
||||
private subscription: Subscription;
|
||||
private postFetcher: any;
|
||||
private emptyState: MainState = {
|
||||
subscribedCommunities: [],
|
||||
trendingCommunities: [],
|
||||
site: {
|
||||
siteRes: {
|
||||
site: {
|
||||
id: null,
|
||||
name: null,
|
||||
|
@ -133,12 +133,11 @@ export class Main extends Component<any, MainState> {
|
|||
|
||||
WebSocketService.Instance.listCommunities(listCommunitiesForm);
|
||||
|
||||
this.keepFetchingPosts();
|
||||
this.fetchPosts();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.subscription.unsubscribe();
|
||||
clearInterval(this.postFetcher);
|
||||
}
|
||||
|
||||
// Necessary for back button for some reason
|
||||
|
@ -159,8 +158,10 @@ export class Main extends Component<any, MainState> {
|
|||
return (
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-8">{this.posts()}</div>
|
||||
<div class="col-12 col-md-4">{this.my_sidebar()}</div>
|
||||
<main role="main" class="col-12 col-md-8">
|
||||
{this.posts()}
|
||||
</main>
|
||||
<aside class="col-12 col-md-4">{this.my_sidebar()}</aside>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -200,7 +201,7 @@ export class Main extends Component<any, MainState> {
|
|||
class="btn btn-sm btn-secondary btn-block"
|
||||
to="/create_community"
|
||||
>
|
||||
<T i18nKey="create_a_community">#</T>
|
||||
{i18n.t('create_a_community')}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -241,7 +242,7 @@ export class Main extends Component<any, MainState> {
|
|||
this.siteInfo()
|
||||
) : (
|
||||
<SiteForm
|
||||
site={this.state.site.site}
|
||||
site={this.state.siteRes.site}
|
||||
onCancel={this.handleEditCancel}
|
||||
/>
|
||||
)}
|
||||
|
@ -262,7 +263,7 @@ export class Main extends Component<any, MainState> {
|
|||
<div>
|
||||
<div class="card border-secondary mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="mb-0">{`${this.state.site.site.name}`}</h5>
|
||||
<h5 class="mb-0">{`${this.state.siteRes.site.name}`}</h5>
|
||||
{this.canAdmin && (
|
||||
<ul class="list-inline mb-1 text-muted small font-weight-bold">
|
||||
<li className="list-inline-item">
|
||||
|
@ -270,74 +271,44 @@ export class Main extends Component<any, MainState> {
|
|||
class="pointer"
|
||||
onClick={linkEvent(this, this.handleEditClick)}
|
||||
>
|
||||
<T i18nKey="edit">#</T>
|
||||
{i18n.t('edit')}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
)}
|
||||
<ul class="my-2 list-inline">
|
||||
<li className="list-inline-item badge badge-secondary">
|
||||
<T
|
||||
i18nKey="number_online"
|
||||
interpolation={{ count: this.state.site.online }}
|
||||
>
|
||||
#
|
||||
</T>
|
||||
{i18n.t('number_online', { count: this.state.siteRes.online })}
|
||||
</li>
|
||||
<li className="list-inline-item badge badge-secondary">
|
||||
<T
|
||||
i18nKey="number_of_users"
|
||||
interpolation={{
|
||||
count: this.state.site.site.number_of_users,
|
||||
}}
|
||||
>
|
||||
#
|
||||
</T>
|
||||
{i18n.t('number_of_users', {
|
||||
count: this.state.siteRes.site.number_of_users,
|
||||
})}
|
||||
</li>
|
||||
<li className="list-inline-item badge badge-secondary">
|
||||
<T
|
||||
i18nKey="number_of_communities"
|
||||
interpolation={{
|
||||
count: this.state.site.site.number_of_communities,
|
||||
}}
|
||||
>
|
||||
#
|
||||
</T>
|
||||
{i18n.t('number_of_communities', {
|
||||
count: this.state.siteRes.site.number_of_communities,
|
||||
})}
|
||||
</li>
|
||||
<li className="list-inline-item badge badge-secondary">
|
||||
<T
|
||||
i18nKey="number_of_posts"
|
||||
interpolation={{
|
||||
count: this.state.site.site.number_of_posts,
|
||||
}}
|
||||
>
|
||||
#
|
||||
</T>
|
||||
{i18n.t('number_of_posts', {
|
||||
count: this.state.siteRes.site.number_of_posts,
|
||||
})}
|
||||
</li>
|
||||
<li className="list-inline-item badge badge-secondary">
|
||||
<T
|
||||
i18nKey="number_of_comments"
|
||||
interpolation={{
|
||||
count: this.state.site.site.number_of_comments,
|
||||
}}
|
||||
>
|
||||
#
|
||||
</T>
|
||||
{i18n.t('number_of_comments', {
|
||||
count: this.state.siteRes.site.number_of_comments,
|
||||
})}
|
||||
</li>
|
||||
<li className="list-inline-item">
|
||||
<Link className="badge badge-secondary" to="/modlog">
|
||||
<T i18nKey="modlog">#</T>
|
||||
{i18n.t('modlog')}
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="mt-1 list-inline small mb-0">
|
||||
<li class="list-inline-item">
|
||||
<T i18nKey="admins" class="d-inline">
|
||||
#
|
||||
</T>
|
||||
:
|
||||
</li>
|
||||
{this.state.site.admins.map(admin => (
|
||||
<li class="list-inline-item">{i18n.t('admins')}:</li>
|
||||
{this.state.siteRes.admins.map(admin => (
|
||||
<li class="list-inline-item">
|
||||
<Link class="text-info" to={`/u/${admin.name}`}>
|
||||
{admin.avatar && showAvatars() && (
|
||||
|
@ -355,13 +326,13 @@ export class Main extends Component<any, MainState> {
|
|||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{this.state.site.site.description && (
|
||||
{this.state.siteRes.site.description && (
|
||||
<div class="card border-secondary mb-3">
|
||||
<div class="card-body">
|
||||
<div
|
||||
className="md-div"
|
||||
dangerouslySetInnerHTML={mdToHtml(
|
||||
this.state.site.site.description
|
||||
this.state.siteRes.site.description
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
@ -376,9 +347,7 @@ export class Main extends Component<any, MainState> {
|
|||
<div class="card border-secondary">
|
||||
<div class="card-body">
|
||||
<h5>
|
||||
<T i18nKey="powered_by" class="d-inline">
|
||||
#
|
||||
</T>
|
||||
{i18n.t('powered_by')}
|
||||
<svg class="icon mx-2">
|
||||
<use xlinkHref="#icon-mouse">#</use>
|
||||
</svg>
|
||||
|
@ -413,7 +382,7 @@ export class Main extends Component<any, MainState> {
|
|||
|
||||
posts() {
|
||||
return (
|
||||
<div>
|
||||
<div class="main-content-wrapper">
|
||||
{this.state.loading ? (
|
||||
<h5>
|
||||
<svg class="icon icon-spinner spin">
|
||||
|
@ -476,7 +445,7 @@ export class Main extends Component<any, MainState> {
|
|||
class="btn btn-sm btn-secondary mr-1"
|
||||
onClick={linkEvent(this, this.prevPage)}
|
||||
>
|
||||
<T i18nKey="prev">#</T>
|
||||
{i18n.t('prev')}
|
||||
</button>
|
||||
)}
|
||||
{this.state.posts.length == fetchLimit && (
|
||||
|
@ -484,7 +453,7 @@ export class Main extends Component<any, MainState> {
|
|||
class="btn btn-sm btn-secondary"
|
||||
onClick={linkEvent(this, this.nextPage)}
|
||||
>
|
||||
<T i18nKey="next">#</T>
|
||||
{i18n.t('next')}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
@ -494,7 +463,7 @@ export class Main extends Component<any, MainState> {
|
|||
get canAdmin(): boolean {
|
||||
return (
|
||||
UserService.Instance.user &&
|
||||
this.state.site.admins
|
||||
this.state.siteRes.admins
|
||||
.map(a => a.id)
|
||||
.includes(UserService.Instance.user.id)
|
||||
);
|
||||
|
@ -548,11 +517,6 @@ export class Main extends Component<any, MainState> {
|
|||
window.scrollTo(0, 0);
|
||||
}
|
||||
|
||||
keepFetchingPosts() {
|
||||
this.fetchPosts();
|
||||
this.postFetcher = setInterval(() => this.fetchPosts(), postRefetchSeconds);
|
||||
}
|
||||
|
||||
fetchPosts() {
|
||||
let getPostsForm: GetPostsForm = {
|
||||
page: this.state.page,
|
||||
|
@ -584,15 +548,15 @@ export class Main extends Component<any, MainState> {
|
|||
if (!data.site) {
|
||||
this.context.router.history.push('/setup');
|
||||
}
|
||||
this.state.site.admins = data.admins;
|
||||
this.state.site.site = data.site;
|
||||
this.state.site.banned = data.banned;
|
||||
this.state.site.online = data.online;
|
||||
this.state.siteRes.admins = data.admins;
|
||||
this.state.siteRes.site = data.site;
|
||||
this.state.siteRes.banned = data.banned;
|
||||
this.state.siteRes.online = data.online;
|
||||
this.setState(this.state);
|
||||
document.title = `${WebSocketService.Instance.site.name}`;
|
||||
} else if (res.op == UserOperation.EditSite) {
|
||||
let data = res.data as SiteResponse;
|
||||
this.state.site.site = data.site;
|
||||
this.state.siteRes.site = data.site;
|
||||
this.state.showEditSite = false;
|
||||
this.setState(this.state);
|
||||
} else if (res.op == UserOperation.GetPosts) {
|
||||
|
@ -600,13 +564,67 @@ export class Main extends Component<any, MainState> {
|
|||
this.state.posts = data.posts;
|
||||
this.state.loading = false;
|
||||
this.setState(this.state);
|
||||
} else if (res.op == UserOperation.CreatePostLike) {
|
||||
let data = res.data as CreatePostLikeResponse;
|
||||
} else if (res.op == UserOperation.CreatePost) {
|
||||
let data = res.data as PostResponse;
|
||||
|
||||
// If you're on subscribed, only push it if you're subscribed.
|
||||
if (this.state.type_ == ListingType.Subscribed) {
|
||||
if (
|
||||
this.state.subscribedCommunities
|
||||
.map(c => c.community_id)
|
||||
.includes(data.post.community_id)
|
||||
) {
|
||||
this.state.posts.unshift(data.post);
|
||||
}
|
||||
} else {
|
||||
this.state.posts.unshift(data.post);
|
||||
}
|
||||
|
||||
this.setState(this.state);
|
||||
} else if (res.op == UserOperation.EditPost) {
|
||||
let data = res.data as PostResponse;
|
||||
let found = this.state.posts.find(c => c.id == data.post.id);
|
||||
found.my_vote = data.post.my_vote;
|
||||
|
||||
found.url = data.post.url;
|
||||
found.name = data.post.name;
|
||||
found.nsfw = data.post.nsfw;
|
||||
|
||||
this.setState(this.state);
|
||||
} else if (res.op == UserOperation.CreatePostLike) {
|
||||
let data = res.data as PostResponse;
|
||||
let found = this.state.posts.find(c => c.id == data.post.id);
|
||||
|
||||
found.score = data.post.score;
|
||||
found.upvotes = data.post.upvotes;
|
||||
found.downvotes = data.post.downvotes;
|
||||
if (data.post.my_vote !== null) {
|
||||
found.my_vote = data.post.my_vote;
|
||||
found.upvoteLoading = false;
|
||||
found.downvoteLoading = false;
|
||||
}
|
||||
|
||||
this.setState(this.state);
|
||||
} else if (res.op == UserOperation.AddAdmin) {
|
||||
let data = res.data as AddAdminResponse;
|
||||
this.state.siteRes.admins = data.admins;
|
||||
this.setState(this.state);
|
||||
} else if (res.op == UserOperation.BanUser) {
|
||||
let data = res.data as BanUserResponse;
|
||||
let found = this.state.siteRes.banned.find(u => (u.id = data.user.id));
|
||||
|
||||
// Remove the banned if its found in the list, and the action is an unban
|
||||
if (found && !data.banned) {
|
||||
this.state.siteRes.banned = this.state.siteRes.banned.filter(
|
||||
i => i.id !== data.user.id
|
||||
);
|
||||
} else {
|
||||
this.state.siteRes.banned.push(data.user);
|
||||
}
|
||||
|
||||
this.state.posts
|
||||
.filter(p => p.creator_id == data.user.id)
|
||||
.forEach(p => (p.banned = data.banned));
|
||||
|
||||
this.setState(this.state);
|
||||
}
|
||||
}
|
||||
|
|
13
ui/src/components/modlog.tsx
vendored
13
ui/src/components/modlog.tsx
vendored
|
@ -15,6 +15,7 @@ import {
|
|||
ModBan,
|
||||
ModAddCommunity,
|
||||
ModAdd,
|
||||
WebSocketJsonResponse,
|
||||
} from '../interfaces';
|
||||
import { WebSocketService } from '../services';
|
||||
import { wsJsonToRes, addTypeInfo, fetchLimit, toast } from '../utils';
|
||||
|
@ -359,15 +360,15 @@ export class Modlog extends Component<any, ModlogState> {
|
|||
/c/{this.state.communityName}{' '}
|
||||
</Link>
|
||||
)}
|
||||
<span>Modlog</span>
|
||||
<span>{i18n.t('modlog')}</span>
|
||||
</h5>
|
||||
<div class="table-responsive">
|
||||
<table id="modlog_table" class="table table-sm table-hover">
|
||||
<thead class="pointer">
|
||||
<tr>
|
||||
<th>Time</th>
|
||||
<th>Mod</th>
|
||||
<th>Action</th>
|
||||
<th> {i18n.t('time')}</th>
|
||||
<th>{i18n.t('mod')}</th>
|
||||
<th>{i18n.t('action')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{this.combined()}
|
||||
|
@ -388,14 +389,14 @@ export class Modlog extends Component<any, ModlogState> {
|
|||
class="btn btn-sm btn-secondary mr-1"
|
||||
onClick={linkEvent(this, this.prevPage)}
|
||||
>
|
||||
Prev
|
||||
{i18n.t('prev')}
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
class="btn btn-sm btn-secondary"
|
||||
onClick={linkEvent(this, this.nextPage)}
|
||||
>
|
||||
Next
|
||||
{i18n.t('next')}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
|
100
ui/src/components/navbar.tsx
vendored
100
ui/src/components/navbar.tsx
vendored
|
@ -14,7 +14,9 @@ import {
|
|||
SortType,
|
||||
GetSiteResponse,
|
||||
Comment,
|
||||
CommentResponse,
|
||||
PrivateMessage,
|
||||
PrivateMessageResponse,
|
||||
WebSocketJsonResponse,
|
||||
} from '../interfaces';
|
||||
import {
|
||||
|
@ -27,7 +29,6 @@ import {
|
|||
} from '../utils';
|
||||
import { version } from '../version';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
interface NavbarState {
|
||||
isLoggedIn: boolean;
|
||||
|
@ -35,7 +36,6 @@ interface NavbarState {
|
|||
replies: Array<Comment>;
|
||||
mentions: Array<Comment>;
|
||||
messages: Array<PrivateMessage>;
|
||||
fetchCount: number;
|
||||
unreadCount: number;
|
||||
siteName: string;
|
||||
}
|
||||
|
@ -46,7 +46,6 @@ export class Navbar extends Component<any, NavbarState> {
|
|||
emptyState: NavbarState = {
|
||||
isLoggedIn: UserService.Instance.user !== undefined,
|
||||
unreadCount: 0,
|
||||
fetchCount: 0,
|
||||
replies: [],
|
||||
mentions: [],
|
||||
messages: [],
|
||||
|
@ -58,8 +57,6 @@ export class Navbar extends Component<any, NavbarState> {
|
|||
super(props, context);
|
||||
this.state = this.emptyState;
|
||||
|
||||
this.keepFetchingUnreads();
|
||||
|
||||
// Subscribe to user changes
|
||||
this.userSub = UserService.Instance.sub.subscribe(user => {
|
||||
this.state.isLoggedIn = user.user !== undefined;
|
||||
|
@ -78,13 +75,15 @@ export class Navbar extends Component<any, NavbarState> {
|
|||
|
||||
if (this.state.isLoggedIn) {
|
||||
this.requestNotificationPermission();
|
||||
// TODO couldn't get re-logging in to re-fetch unreads
|
||||
this.fetchUnreads();
|
||||
}
|
||||
|
||||
WebSocketService.Instance.getSite();
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div>{this.navbar()}</div>;
|
||||
return this.navbar();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
@ -102,6 +101,7 @@ export class Navbar extends Component<any, NavbarState> {
|
|||
<button
|
||||
class="navbar-toggler"
|
||||
type="button"
|
||||
aria-label="menu"
|
||||
onClick={linkEvent(this, this.expandNavbar)}
|
||||
>
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
|
@ -112,12 +112,12 @@ export class Navbar extends Component<any, NavbarState> {
|
|||
<ul class="navbar-nav mr-auto">
|
||||
<li class="nav-item">
|
||||
<Link class="nav-link" to="/communities">
|
||||
<T i18nKey="communities">#</T>
|
||||
{i18n.t('communities')}
|
||||
</Link>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<Link class="nav-link" to="/search">
|
||||
<T i18nKey="search">#</T>
|
||||
{i18n.t('search')}
|
||||
</Link>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
|
@ -128,12 +128,12 @@ export class Navbar extends Component<any, NavbarState> {
|
|||
state: { prevPath: this.currentLocation },
|
||||
}}
|
||||
>
|
||||
<T i18nKey="create_post">#</T>
|
||||
{i18n.t('create_post')}
|
||||
</Link>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<Link class="nav-link" to="/create_community">
|
||||
<T i18nKey="create_community">#</T>
|
||||
{i18n.t('create_community')}
|
||||
</Link>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
|
@ -186,7 +186,7 @@ export class Navbar extends Component<any, NavbarState> {
|
|||
</>
|
||||
) : (
|
||||
<Link class="nav-link" to="/login">
|
||||
<T i18nKey="login_sign_up">#</T>
|
||||
{i18n.t('login_sign_up')}
|
||||
</Link>
|
||||
)}
|
||||
</ul>
|
||||
|
@ -211,45 +211,51 @@ export class Navbar extends Component<any, NavbarState> {
|
|||
} else if (res.op == UserOperation.GetReplies) {
|
||||
let data = res.data as GetRepliesResponse;
|
||||
let unreadReplies = data.replies.filter(r => !r.read);
|
||||
if (
|
||||
unreadReplies.length > 0 &&
|
||||
this.state.fetchCount > 1 &&
|
||||
JSON.stringify(this.state.replies) !== JSON.stringify(unreadReplies)
|
||||
) {
|
||||
this.notify(unreadReplies);
|
||||
}
|
||||
|
||||
this.state.replies = unreadReplies;
|
||||
this.state.unreadCount = this.calculateUnreadCount();
|
||||
this.setState(this.state);
|
||||
this.sendUnreadCount();
|
||||
} else if (res.op == UserOperation.GetUserMentions) {
|
||||
let data = res.data as GetUserMentionsResponse;
|
||||
let unreadMentions = data.mentions.filter(r => !r.read);
|
||||
if (
|
||||
unreadMentions.length > 0 &&
|
||||
this.state.fetchCount > 1 &&
|
||||
JSON.stringify(this.state.mentions) !== JSON.stringify(unreadMentions)
|
||||
) {
|
||||
this.notify(unreadMentions);
|
||||
}
|
||||
|
||||
this.state.mentions = unreadMentions;
|
||||
this.state.unreadCount = this.calculateUnreadCount();
|
||||
this.setState(this.state);
|
||||
this.sendUnreadCount();
|
||||
} else if (res.op == UserOperation.GetPrivateMessages) {
|
||||
let data = res.data as PrivateMessagesResponse;
|
||||
let unreadMessages = data.messages.filter(r => !r.read);
|
||||
if (
|
||||
unreadMessages.length > 0 &&
|
||||
this.state.fetchCount > 1 &&
|
||||
JSON.stringify(this.state.messages) !== JSON.stringify(unreadMessages)
|
||||
) {
|
||||
this.notify(unreadMessages);
|
||||
}
|
||||
|
||||
this.state.messages = unreadMessages;
|
||||
this.state.unreadCount = this.calculateUnreadCount();
|
||||
this.setState(this.state);
|
||||
this.sendUnreadCount();
|
||||
} else if (res.op == UserOperation.CreateComment) {
|
||||
let data = res.data as CommentResponse;
|
||||
|
||||
if (this.state.isLoggedIn) {
|
||||
if (data.recipient_ids.includes(UserService.Instance.user.id)) {
|
||||
this.state.replies.push(data.comment);
|
||||
this.state.unreadCount++;
|
||||
this.setState(this.state);
|
||||
this.sendUnreadCount();
|
||||
this.notify(data.comment);
|
||||
}
|
||||
}
|
||||
} else if (res.op == UserOperation.CreatePrivateMessage) {
|
||||
let data = res.data as PrivateMessageResponse;
|
||||
|
||||
if (this.state.isLoggedIn) {
|
||||
if (data.message.recipient_id == UserService.Instance.user.id) {
|
||||
this.state.messages.push(data.message);
|
||||
this.state.unreadCount++;
|
||||
this.setState(this.state);
|
||||
this.sendUnreadCount();
|
||||
this.notify(data.message);
|
||||
}
|
||||
}
|
||||
} else if (res.op == UserOperation.GetSite) {
|
||||
let data = res.data as GetSiteResponse;
|
||||
|
||||
|
@ -261,11 +267,6 @@ export class Navbar extends Component<any, NavbarState> {
|
|||
}
|
||||
}
|
||||
|
||||
keepFetchingUnreads() {
|
||||
this.fetchUnreads();
|
||||
setInterval(() => this.fetchUnreads(), 15000);
|
||||
}
|
||||
|
||||
fetchUnreads() {
|
||||
if (this.state.isLoggedIn) {
|
||||
let repliesForm: GetRepliesForm = {
|
||||
|
@ -292,7 +293,6 @@ export class Navbar extends Component<any, NavbarState> {
|
|||
WebSocketService.Instance.getReplies(repliesForm);
|
||||
WebSocketService.Instance.getUserMentions(userMentionsForm);
|
||||
WebSocketService.Instance.getPrivateMessages(privateMessagesForm);
|
||||
this.state.fetchCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -304,11 +304,11 @@ export class Navbar extends Component<any, NavbarState> {
|
|||
sendUnreadCount() {
|
||||
UserService.Instance.sub.next({
|
||||
user: UserService.Instance.user,
|
||||
unreadCount: this.unreadCount,
|
||||
unreadCount: this.state.unreadCount,
|
||||
});
|
||||
}
|
||||
|
||||
get unreadCount() {
|
||||
calculateUnreadCount(): number {
|
||||
return (
|
||||
this.state.replies.filter(r => !r.read).length +
|
||||
this.state.mentions.filter(r => !r.read).length +
|
||||
|
@ -330,24 +330,20 @@ export class Navbar extends Component<any, NavbarState> {
|
|||
}
|
||||
}
|
||||
|
||||
notify(replies: Array<Comment | PrivateMessage>) {
|
||||
let recentReply = replies[0];
|
||||
notify(reply: Comment | PrivateMessage) {
|
||||
if (Notification.permission !== 'granted') Notification.requestPermission();
|
||||
else {
|
||||
var notification = new Notification(
|
||||
`${replies.length} ${i18n.t('unread_messages')}`,
|
||||
{
|
||||
icon: recentReply.creator_avatar
|
||||
? recentReply.creator_avatar
|
||||
var notification = new Notification(reply.creator_name, {
|
||||
icon: reply.creator_avatar
|
||||
? reply.creator_avatar
|
||||
: `${window.location.protocol}//${window.location.host}/static/assets/apple-touch-icon.png`,
|
||||
body: `${recentReply.creator_name}: ${recentReply.content}`,
|
||||
}
|
||||
);
|
||||
body: `${reply.content}`,
|
||||
});
|
||||
|
||||
notification.onclick = () => {
|
||||
this.context.router.history.push(
|
||||
isCommentType(recentReply)
|
||||
? `/post/${recentReply.post_id}/comment/${recentReply.id}`
|
||||
isCommentType(reply)
|
||||
? `/post/${reply.post_id}/comment/${reply.id}`
|
||||
: `/inbox`
|
||||
);
|
||||
};
|
||||
|
|
9
ui/src/components/password_change.tsx
vendored
9
ui/src/components/password_change.tsx
vendored
|
@ -10,7 +10,6 @@ import {
|
|||
import { WebSocketService, UserService } from '../services';
|
||||
import { wsJsonToRes, capitalizeFirstLetter, toast } from '../utils';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
interface State {
|
||||
passwordChangeForm: PasswordChangeForm;
|
||||
|
@ -58,9 +57,7 @@ export class PasswordChange extends Component<any, State> {
|
|||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-6 offset-lg-3 mb-4">
|
||||
<h5>
|
||||
<T i18nKey="password_change">#</T>
|
||||
</h5>
|
||||
<h5>{i18n.t('password_change')}</h5>
|
||||
{this.passwordChangeForm()}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -73,7 +70,7 @@ export class PasswordChange extends Component<any, State> {
|
|||
<form onSubmit={linkEvent(this, this.handlePasswordChangeSubmit)}>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">
|
||||
<T i18nKey="new_password">#</T>
|
||||
{i18n.t('new_password')}
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
|
@ -87,7 +84,7 @@ export class PasswordChange extends Component<any, State> {
|
|||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">
|
||||
<T i18nKey="verify_password">#</T>
|
||||
{i18n.t('verify_password')}
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
|
|
49
ui/src/components/post-form.tsx
vendored
49
ui/src/components/post-form.tsx
vendored
|
@ -36,7 +36,6 @@ import {
|
|||
import autosize from 'autosize';
|
||||
import Tribute from 'tributejs/src/Tribute.js';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
interface PostFormProps {
|
||||
post?: Post; // If a post is given, that means this is an edit
|
||||
|
@ -151,12 +150,13 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
|||
<div>
|
||||
<form onSubmit={linkEvent(this, this.handlePostSubmit)}>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">
|
||||
<T i18nKey="url">#</T>
|
||||
<label class="col-sm-2 col-form-label" htmlFor="post-url">
|
||||
{i18n.t('url')}
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="url"
|
||||
id="post-url"
|
||||
class="form-control"
|
||||
value={this.state.postForm.url}
|
||||
onInput={linkEvent(this, this.handlePostUrlChange)}
|
||||
|
@ -167,12 +167,9 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
|||
class="mt-1 text-muted small font-weight-bold pointer"
|
||||
onClick={linkEvent(this, this.copySuggestedTitle)}
|
||||
>
|
||||
<T
|
||||
i18nKey="copy_suggested_title"
|
||||
interpolation={{ title: this.state.suggestedTitle }}
|
||||
>
|
||||
#
|
||||
</T>
|
||||
{i18n.t('copy_suggested_title', {
|
||||
title: this.state.suggestedTitle,
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
<form>
|
||||
|
@ -181,7 +178,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
|||
className={`${UserService.Instance.user &&
|
||||
'pointer'} d-inline-block mr-2 float-right text-muted small font-weight-bold`}
|
||||
>
|
||||
<T i18nKey="upload_image">#</T>
|
||||
{i18n.t('upload_image')}
|
||||
</label>
|
||||
<input
|
||||
id="file-upload"
|
||||
|
@ -201,7 +198,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
|||
target="_blank"
|
||||
class="mr-2 d-inline-block float-right text-muted small font-weight-bold"
|
||||
>
|
||||
<T i18nKey="archive_link">#</T>
|
||||
{i18n.t('archive_link')}
|
||||
</a>
|
||||
)}
|
||||
{this.state.imageLoading && (
|
||||
|
@ -215,7 +212,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
|||
{this.state.crossPosts.length > 0 && (
|
||||
<>
|
||||
<div class="my-1 text-muted small font-weight-bold">
|
||||
<T i18nKey="cross_posts">#</T>
|
||||
{i18n.t('cross_posts')}
|
||||
</div>
|
||||
<PostListings showCommunity posts={this.state.crossPosts} />
|
||||
</>
|
||||
|
@ -223,12 +220,13 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
|||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">
|
||||
<T i18nKey="title">#</T>
|
||||
<label class="col-sm-2 col-form-label" htmlFor="post-title">
|
||||
{i18n.t('title')}
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<textarea
|
||||
value={this.state.postForm.name}
|
||||
id="post-title"
|
||||
onInput={linkEvent(this, this.handlePostNameChange)}
|
||||
class="form-control"
|
||||
required
|
||||
|
@ -239,16 +237,17 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
|||
{this.state.suggestedPosts.length > 0 && (
|
||||
<>
|
||||
<div class="my-1 text-muted small font-weight-bold">
|
||||
<T i18nKey="related_posts">#</T>
|
||||
{i18n.t('related_posts')}
|
||||
</div>
|
||||
<PostListings posts={this.state.suggestedPosts} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">
|
||||
<T i18nKey="body">#</T>
|
||||
<label class="col-sm-2 col-form-label" htmlFor={this.id}>
|
||||
{i18n.t('body')}
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<textarea
|
||||
|
@ -271,7 +270,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
|||
.previewMode && 'active'}`}
|
||||
onClick={linkEvent(this, this.handlePreviewToggle)}
|
||||
>
|
||||
<T i18nKey="preview">#</T>
|
||||
{i18n.t('preview')}
|
||||
</button>
|
||||
)}
|
||||
<a
|
||||
|
@ -279,18 +278,19 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
|||
target="_blank"
|
||||
class="d-inline-block float-right text-muted small font-weight-bold"
|
||||
>
|
||||
<T i18nKey="formatting_help">#</T>
|
||||
{i18n.t('formatting_help')}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{!this.props.post && (
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">
|
||||
<T i18nKey="community">#</T>
|
||||
<label class="col-sm-2 col-form-label" htmlFor="post-community">
|
||||
{i18n.t('community')}
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<select
|
||||
class="form-control"
|
||||
id="post-community"
|
||||
value={this.state.postForm.community_id}
|
||||
onInput={linkEvent(this, this.handlePostCommunityChange)}
|
||||
>
|
||||
|
@ -307,12 +307,13 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
|||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
id="post-nsfw"
|
||||
type="checkbox"
|
||||
checked={this.state.postForm.nsfw}
|
||||
onChange={linkEvent(this, this.handlePostNsfwChange)}
|
||||
/>
|
||||
<label class="form-check-label">
|
||||
<T i18nKey="nsfw">#</T>
|
||||
<label class="form-check-label" htmlFor="post-nsfw">
|
||||
{i18n.t('nsfw')}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -337,7 +338,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
|||
class="btn btn-secondary"
|
||||
onClick={linkEvent(this, this.handleCancel)}
|
||||
>
|
||||
<T i18nKey="cancel">#</T>
|
||||
{i18n.t('cancel')}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
|
73
ui/src/components/post-listing.tsx
vendored
73
ui/src/components/post-listing.tsx
vendored
|
@ -30,7 +30,6 @@ import {
|
|||
imageThumbnailer,
|
||||
} from '../utils';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
interface PostListingState {
|
||||
showEdit: boolean;
|
||||
|
@ -119,7 +118,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
<div class="listing col-12">
|
||||
<div className={`vote-bar mr-2 float-left small text-center`}>
|
||||
<button
|
||||
className={`btn p-0 ${
|
||||
className={`btn btn-link p-0 ${
|
||||
post.my_vote == 1 ? 'text-info' : 'text-muted'
|
||||
}`}
|
||||
onClick={linkEvent(this, this.handlePostLike)}
|
||||
|
@ -137,7 +136,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
<div class={`font-weight-bold text-muted`}>{post.score}</div>
|
||||
{WebSocketService.Instance.site.enable_downvotes && (
|
||||
<button
|
||||
className={`btn p-0 ${
|
||||
className={`btn btn-link p-0 ${
|
||||
post.my_vote == -1 ? 'text-danger' : 'text-muted'
|
||||
}`}
|
||||
onClick={linkEvent(this, this.handlePostDisLike)}
|
||||
|
@ -247,27 +246,27 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
)}
|
||||
{post.removed && (
|
||||
<small className="ml-2 text-muted font-italic">
|
||||
<T i18nKey="removed">#</T>
|
||||
{i18n.t('removed')}
|
||||
</small>
|
||||
)}
|
||||
{post.deleted && (
|
||||
<small className="ml-2 text-muted font-italic">
|
||||
<T i18nKey="deleted">#</T>
|
||||
{i18n.t('deleted')}
|
||||
</small>
|
||||
)}
|
||||
{post.locked && (
|
||||
<small className="ml-2 text-muted font-italic">
|
||||
<T i18nKey="locked">#</T>
|
||||
{i18n.t('locked')}
|
||||
</small>
|
||||
)}
|
||||
{post.stickied && (
|
||||
<small className="ml-2 text-muted font-italic">
|
||||
<T i18nKey="stickied">#</T>
|
||||
{i18n.t('stickied')}
|
||||
</small>
|
||||
)}
|
||||
{post.nsfw && (
|
||||
<small className="ml-2 text-muted font-italic">
|
||||
<T i18nKey="nsfw">#</T>
|
||||
{i18n.t('nsfw')}
|
||||
</small>
|
||||
)}
|
||||
</div>
|
||||
|
@ -288,18 +287,16 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
<span>{post.creator_name}</span>
|
||||
</Link>
|
||||
{this.isMod && (
|
||||
<span className="mx-1 badge badge-light">
|
||||
<T i18nKey="mod">#</T>
|
||||
</span>
|
||||
<span className="mx-1 badge badge-light">{i18n.t('mod')}</span>
|
||||
)}
|
||||
{this.isAdmin && (
|
||||
<span className="mx-1 badge badge-light">
|
||||
<T i18nKey="admin">#</T>
|
||||
{i18n.t('admin')}
|
||||
</span>
|
||||
)}
|
||||
{(post.banned_from_community || post.banned) && (
|
||||
<span className="mx-1 badge badge-danger">
|
||||
<T i18nKey="banned">#</T>
|
||||
{i18n.t('banned')}
|
||||
</span>
|
||||
)}
|
||||
{this.props.showCommunity && (
|
||||
|
@ -326,12 +323,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
</li>
|
||||
<li className="list-inline-item">
|
||||
<Link className="text-muted" to={`/post/${post.id}`}>
|
||||
<T
|
||||
i18nKey="number_of_comments"
|
||||
interpolation={{ count: post.number_of_comments }}
|
||||
>
|
||||
#
|
||||
</T>
|
||||
{i18n.t('number_of_comments', {
|
||||
count: post.number_of_comments,
|
||||
})}
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -353,7 +347,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
className="text-muted"
|
||||
to={`/create_post${this.crossPostParams}`}
|
||||
>
|
||||
<T i18nKey="cross_post">#</T>
|
||||
{i18n.t('cross_post')}
|
||||
</Link>
|
||||
</li>
|
||||
</>
|
||||
|
@ -365,7 +359,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
class="pointer"
|
||||
onClick={linkEvent(this, this.handleEditClick)}
|
||||
>
|
||||
<T i18nKey="edit">#</T>
|
||||
{i18n.t('edit')}
|
||||
</span>
|
||||
</li>
|
||||
<li className="list-inline-item mr-2">
|
||||
|
@ -406,14 +400,14 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
class="pointer"
|
||||
onClick={linkEvent(this, this.handleModRemoveShow)}
|
||||
>
|
||||
<T i18nKey="remove">#</T>
|
||||
{i18n.t('remove')}
|
||||
</span>
|
||||
) : (
|
||||
<span
|
||||
class="pointer"
|
||||
onClick={linkEvent(this, this.handleModRemoveSubmit)}
|
||||
>
|
||||
<T i18nKey="restore">#</T>
|
||||
{i18n.t('restore')}
|
||||
</span>
|
||||
)}
|
||||
</li>
|
||||
|
@ -430,7 +424,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
this.handleModBanFromCommunityShow
|
||||
)}
|
||||
>
|
||||
<T i18nKey="ban">#</T>
|
||||
{i18n.t('ban')}
|
||||
</span>
|
||||
) : (
|
||||
<span
|
||||
|
@ -440,7 +434,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
this.handleModBanFromCommunitySubmit
|
||||
)}
|
||||
>
|
||||
<T i18nKey="unban">#</T>
|
||||
{i18n.t('unban')}
|
||||
</span>
|
||||
)}
|
||||
</li>
|
||||
|
@ -473,12 +467,12 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
this.handleShowConfirmTransferCommunity
|
||||
)}
|
||||
>
|
||||
<T i18nKey="transfer_community">#</T>
|
||||
{i18n.t('transfer_community')}
|
||||
</span>
|
||||
) : (
|
||||
<>
|
||||
<span class="d-inline-block mr-1">
|
||||
<T i18nKey="are_you_sure">#</T>
|
||||
{i18n.t('are_you_sure')}
|
||||
</span>
|
||||
<span
|
||||
class="pointer d-inline-block mr-1"
|
||||
|
@ -487,7 +481,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
this.handleTransferCommunity
|
||||
)}
|
||||
>
|
||||
<T i18nKey="yes">#</T>
|
||||
{i18n.t('yes')}
|
||||
</span>
|
||||
<span
|
||||
class="pointer d-inline-block"
|
||||
|
@ -496,7 +490,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
this.handleCancelShowConfirmTransferCommunity
|
||||
)}
|
||||
>
|
||||
<T i18nKey="no">#</T>
|
||||
{i18n.t('no')}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
|
@ -512,14 +506,14 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
class="pointer"
|
||||
onClick={linkEvent(this, this.handleModBanShow)}
|
||||
>
|
||||
<T i18nKey="ban_from_site">#</T>
|
||||
{i18n.t('ban_from_site')}
|
||||
</span>
|
||||
) : (
|
||||
<span
|
||||
class="pointer"
|
||||
onClick={linkEvent(this, this.handleModBanSubmit)}
|
||||
>
|
||||
<T i18nKey="unban_from_site">#</T>
|
||||
{i18n.t('unban_from_site')}
|
||||
</span>
|
||||
)}
|
||||
</li>
|
||||
|
@ -549,18 +543,18 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
this.handleShowConfirmTransferSite
|
||||
)}
|
||||
>
|
||||
<T i18nKey="transfer_site">#</T>
|
||||
{i18n.t('transfer_site')}
|
||||
</span>
|
||||
) : (
|
||||
<>
|
||||
<span class="d-inline-block mr-1">
|
||||
<T i18nKey="are_you_sure">#</T>
|
||||
{i18n.t('are_you_sure')}
|
||||
</span>
|
||||
<span
|
||||
class="pointer d-inline-block mr-1"
|
||||
onClick={linkEvent(this, this.handleTransferSite)}
|
||||
>
|
||||
<T i18nKey="yes">#</T>
|
||||
{i18n.t('yes')}
|
||||
</span>
|
||||
<span
|
||||
class="pointer d-inline-block"
|
||||
|
@ -569,7 +563,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
this.handleCancelShowConfirmTransferSite
|
||||
)}
|
||||
>
|
||||
<T i18nKey="no">#</T>
|
||||
{i18n.t('no')}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
|
@ -583,7 +577,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
className="pointer"
|
||||
onClick={linkEvent(this, this.handleViewSource)}
|
||||
>
|
||||
<T i18nKey="view_source">#</T>
|
||||
{i18n.t('view_source')}
|
||||
</span>
|
||||
</li>
|
||||
)}
|
||||
|
@ -601,18 +595,19 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
onInput={linkEvent(this, this.handleModRemoveReasonChange)}
|
||||
/>
|
||||
<button type="submit" class="btn btn-secondary">
|
||||
<T i18nKey="remove_post">#</T>
|
||||
{i18n.t('remove_post')}
|
||||
</button>
|
||||
</form>
|
||||
)}
|
||||
{this.state.showBanDialog && (
|
||||
<form onSubmit={linkEvent(this, this.handleModBanBothSubmit)}>
|
||||
<div class="form-group row">
|
||||
<label class="col-form-label">
|
||||
<T i18nKey="reason">#</T>
|
||||
<label class="col-form-label" htmlFor="post-listing-reason">
|
||||
{i18n.t('reason')}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="post-listing-reason"
|
||||
class="form-control mr-2"
|
||||
placeholder={i18n.t('reason')}
|
||||
value={this.state.banReason}
|
||||
|
|
12
ui/src/components/post-listings.tsx
vendored
12
ui/src/components/post-listings.tsx
vendored
|
@ -2,7 +2,7 @@ import { Component } from 'inferno';
|
|||
import { Link } from 'inferno-router';
|
||||
import { Post } from '../interfaces';
|
||||
import { PostListing } from './post-listing';
|
||||
import { T } from 'inferno-i18next';
|
||||
import { i18n } from '../i18next';
|
||||
|
||||
interface PostListingsProps {
|
||||
posts: Array<Post>;
|
||||
|
@ -30,14 +30,12 @@ export class PostListings extends Component<PostListingsProps, any> {
|
|||
))
|
||||
) : (
|
||||
<>
|
||||
<div>
|
||||
<T i18nKey="no_posts">#</T>
|
||||
</div>
|
||||
<div>{i18n.t('no_posts')}</div>
|
||||
{this.props.showCommunity !== undefined && (
|
||||
<div>
|
||||
<T i18nKey="subscribe_to_communities">
|
||||
#<Link to="/communities">#</Link>
|
||||
</T>
|
||||
<Link to="/communities">
|
||||
{i18n.t('subscribe_to_communities')}
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
|
25
ui/src/components/post.tsx
vendored
25
ui/src/components/post.tsx
vendored
|
@ -11,7 +11,6 @@ import {
|
|||
CommentForm as CommentFormI,
|
||||
CommentResponse,
|
||||
CommentSortType,
|
||||
CreatePostLikeResponse,
|
||||
CommunityUser,
|
||||
CommunityResponse,
|
||||
CommentNode as CommentNodeI,
|
||||
|
@ -38,7 +37,6 @@ import { CommentForm } from './comment-form';
|
|||
import { CommentNodes } from './comment-nodes';
|
||||
import autosize from 'autosize';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
interface PostState {
|
||||
post: PostI;
|
||||
|
@ -47,6 +45,7 @@ interface PostState {
|
|||
community: Community;
|
||||
moderators: Array<CommunityUser>;
|
||||
admins: Array<UserView>;
|
||||
online: number;
|
||||
scrolled?: boolean;
|
||||
scrolled_comment_id?: number;
|
||||
loading: boolean;
|
||||
|
@ -62,6 +61,7 @@ export class Post extends Component<any, PostState> {
|
|||
community: null,
|
||||
moderators: [],
|
||||
admins: [],
|
||||
online: null,
|
||||
scrolled: false,
|
||||
loading: true,
|
||||
crossPosts: [],
|
||||
|
@ -173,7 +173,7 @@ export class Post extends Component<any, PostState> {
|
|||
{this.state.crossPosts.length > 0 && (
|
||||
<>
|
||||
<div class="my-1 text-muted small font-weight-bold">
|
||||
<T i18nKey="cross_posts">#</T>
|
||||
{i18n.t('cross_posts')}
|
||||
</div>
|
||||
<PostListings showCommunity posts={this.state.crossPosts} />
|
||||
</>
|
||||
|
@ -255,9 +255,7 @@ export class Post extends Component<any, PostState> {
|
|||
return (
|
||||
<div class="d-none d-md-block new-comments mb-3 card border-secondary">
|
||||
<div class="card-body small">
|
||||
<h6>
|
||||
<T i18nKey="recent_comments">#</T>
|
||||
</h6>
|
||||
<h6>{i18n.t('recent_comments')}</h6>
|
||||
{this.state.comments.map(comment => (
|
||||
<CommentNodes
|
||||
nodes={[{ comment: comment }]}
|
||||
|
@ -280,6 +278,7 @@ export class Post extends Component<any, PostState> {
|
|||
community={this.state.community}
|
||||
moderators={this.state.moderators}
|
||||
admins={this.state.admins}
|
||||
online={this.state.online}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -378,6 +377,7 @@ export class Post extends Component<any, PostState> {
|
|||
this.state.community = data.community;
|
||||
this.state.moderators = data.moderators;
|
||||
this.state.admins = data.admins;
|
||||
this.state.online = data.online;
|
||||
this.state.loading = false;
|
||||
document.title = `${this.state.post.name} - ${WebSocketService.Instance.site.name}`;
|
||||
|
||||
|
@ -396,8 +396,12 @@ export class Post extends Component<any, PostState> {
|
|||
this.setState(this.state);
|
||||
} else if (res.op == UserOperation.CreateComment) {
|
||||
let data = res.data as CommentResponse;
|
||||
|
||||
// Necessary since it might be a user reply
|
||||
if (data.recipient_ids.length == 0) {
|
||||
this.state.comments.unshift(data.comment);
|
||||
this.setState(this.state);
|
||||
}
|
||||
} else if (res.op == UserOperation.EditComment) {
|
||||
let data = res.data as CommentResponse;
|
||||
let found = this.state.comments.find(c => c.id == data.comment.id);
|
||||
|
@ -431,11 +435,16 @@ export class Post extends Component<any, PostState> {
|
|||
}
|
||||
this.setState(this.state);
|
||||
} else if (res.op == UserOperation.CreatePostLike) {
|
||||
let data = res.data as CreatePostLikeResponse;
|
||||
this.state.post.my_vote = data.post.my_vote;
|
||||
let data = res.data as PostResponse;
|
||||
this.state.post.score = data.post.score;
|
||||
this.state.post.upvotes = data.post.upvotes;
|
||||
this.state.post.downvotes = data.post.downvotes;
|
||||
if (data.post.my_vote !== null) {
|
||||
this.state.post.my_vote = data.post.my_vote;
|
||||
this.state.post.upvoteLoading = false;
|
||||
this.state.post.downvoteLoading = false;
|
||||
}
|
||||
|
||||
this.setState(this.state);
|
||||
} else if (res.op == UserOperation.EditPost) {
|
||||
let data = res.data as PostResponse;
|
||||
|
|
7
ui/src/components/private-message.tsx
vendored
7
ui/src/components/private-message.tsx
vendored
|
@ -14,7 +14,6 @@ import {
|
|||
import { MomentTime } from './moment-time';
|
||||
import { PrivateMessageForm } from './private-message-form';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
interface PrivateMessageState {
|
||||
showReply: boolean;
|
||||
|
@ -140,7 +139,7 @@ export class PrivateMessage extends Component<
|
|||
class="pointer"
|
||||
onClick={linkEvent(this, this.handleReplyClick)}
|
||||
>
|
||||
<T i18nKey="reply">#</T>
|
||||
{i18n.t('reply')}
|
||||
</span>
|
||||
</li>
|
||||
</>
|
||||
|
@ -152,7 +151,7 @@ export class PrivateMessage extends Component<
|
|||
class="pointer"
|
||||
onClick={linkEvent(this, this.handleEditClick)}
|
||||
>
|
||||
<T i18nKey="edit">#</T>
|
||||
{i18n.t('edit')}
|
||||
</span>
|
||||
</li>
|
||||
<li className="list-inline-item">
|
||||
|
@ -173,7 +172,7 @@ export class PrivateMessage extends Component<
|
|||
className="pointer"
|
||||
onClick={linkEvent(this, this.handleViewSource)}
|
||||
>
|
||||
<T i18nKey="view_source">#</T>
|
||||
{i18n.t('view_source')}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
45
ui/src/components/search.tsx
vendored
45
ui/src/components/search.tsx
vendored
|
@ -12,7 +12,7 @@ import {
|
|||
SearchForm,
|
||||
SearchResponse,
|
||||
SearchType,
|
||||
CreatePostLikeResponse,
|
||||
PostResponse,
|
||||
CommentResponse,
|
||||
WebSocketJsonResponse,
|
||||
} from '../interfaces';
|
||||
|
@ -30,7 +30,6 @@ import { PostListing } from './post-listing';
|
|||
import { SortSelect } from './sort-select';
|
||||
import { CommentNodes } from './comment-nodes';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
interface SearchState {
|
||||
q: string;
|
||||
|
@ -126,9 +125,7 @@ export class Search extends Component<any, SearchState> {
|
|||
render() {
|
||||
return (
|
||||
<div class="container">
|
||||
<h5>
|
||||
<T i18nKey="search">#</T>
|
||||
</h5>
|
||||
<h5>{i18n.t('search')}</h5>
|
||||
{this.selects()}
|
||||
{this.searchForm()}
|
||||
{this.state.type_ == SearchType.All && this.all()}
|
||||
|
@ -163,9 +160,7 @@ export class Search extends Component<any, SearchState> {
|
|||
<use xlinkHref="#icon-spinner"></use>
|
||||
</svg>
|
||||
) : (
|
||||
<span>
|
||||
<T i18nKey="search">#</T>
|
||||
</span>
|
||||
<span>{i18n.t('search')}</span>
|
||||
)}
|
||||
</button>
|
||||
</form>
|
||||
|
@ -180,24 +175,14 @@ export class Search extends Component<any, SearchState> {
|
|||
onChange={linkEvent(this, this.handleTypeChange)}
|
||||
class="custom-select custom-select-sm w-auto"
|
||||
>
|
||||
<option disabled>
|
||||
<T i18nKey="type">#</T>
|
||||
</option>
|
||||
<option value={SearchType.All}>
|
||||
<T i18nKey="all">#</T>
|
||||
</option>
|
||||
<option value={SearchType.Comments}>
|
||||
<T i18nKey="comments">#</T>
|
||||
</option>
|
||||
<option value={SearchType.Posts}>
|
||||
<T i18nKey="posts">#</T>
|
||||
</option>
|
||||
<option disabled>{i18n.t('type')}</option>
|
||||
<option value={SearchType.All}>{i18n.t('all')}</option>
|
||||
<option value={SearchType.Comments}>{i18n.t('comments')}</option>
|
||||
<option value={SearchType.Posts}>{i18n.t('posts')}</option>
|
||||
<option value={SearchType.Communities}>
|
||||
<T i18nKey="communities">#</T>
|
||||
</option>
|
||||
<option value={SearchType.Users}>
|
||||
<T i18nKey="users">#</T>
|
||||
{i18n.t('communities')}
|
||||
</option>
|
||||
<option value={SearchType.Users}>{i18n.t('users')}</option>
|
||||
</select>
|
||||
<span class="ml-2">
|
||||
<SortSelect
|
||||
|
@ -383,14 +368,14 @@ export class Search extends Component<any, SearchState> {
|
|||
class="btn btn-sm btn-secondary mr-1"
|
||||
onClick={linkEvent(this, this.prevPage)}
|
||||
>
|
||||
<T i18nKey="prev">#</T>
|
||||
{i18n.t('prev')}
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
class="btn btn-sm btn-secondary"
|
||||
onClick={linkEvent(this, this.nextPage)}
|
||||
>
|
||||
<T i18nKey="next">#</T>
|
||||
{i18n.t('next')}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
@ -404,11 +389,7 @@ export class Search extends Component<any, SearchState> {
|
|||
res.posts.length == 0 &&
|
||||
res.comments.length == 0 &&
|
||||
res.communities.length == 0 &&
|
||||
res.users.length == 0 && (
|
||||
<span>
|
||||
<T i18nKey="no_results">#</T>
|
||||
</span>
|
||||
)}
|
||||
res.users.length == 0 && <span>{i18n.t('no_results')}</span>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -506,7 +487,7 @@ export class Search extends Component<any, SearchState> {
|
|||
}
|
||||
this.setState(this.state);
|
||||
} else if (res.op == UserOperation.CreatePostLike) {
|
||||
let data = res.data as CreatePostLikeResponse;
|
||||
let data = res.data as PostResponse;
|
||||
let found = this.state.searchResponse.posts.find(
|
||||
c => c.id == data.post.id
|
||||
);
|
||||
|
|
30
ui/src/components/setup.tsx
vendored
30
ui/src/components/setup.tsx
vendored
|
@ -11,7 +11,6 @@ import { WebSocketService, UserService } from '../services';
|
|||
import { wsJsonToRes, toast } from '../utils';
|
||||
import { SiteForm } from './site-form';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
interface State {
|
||||
userForm: RegisterForm;
|
||||
|
@ -61,9 +60,7 @@ export class Setup extends Component<any, State> {
|
|||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12 offset-lg-3 col-lg-6">
|
||||
<h3>
|
||||
<T i18nKey="lemmy_instance_setup">#</T>
|
||||
</h3>
|
||||
<h3>{i18n.t('lemmy_instance_setup')}</h3>
|
||||
{!this.state.doneRegisteringUser ? (
|
||||
this.registerUser()
|
||||
) : (
|
||||
|
@ -78,17 +75,16 @@ export class Setup extends Component<any, State> {
|
|||
registerUser() {
|
||||
return (
|
||||
<form onSubmit={linkEvent(this, this.handleRegisterSubmit)}>
|
||||
<h5>
|
||||
<T i18nKey="setup_admin">#</T>
|
||||
</h5>
|
||||
<h5>{i18n.t('setup_admin')}</h5>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">
|
||||
<T i18nKey="username">#</T>
|
||||
<label class="col-sm-2 col-form-label" htmlFor="username">
|
||||
{i18n.t('username')}
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="username"
|
||||
value={this.state.userForm.username}
|
||||
onInput={linkEvent(this, this.handleRegisterUsernameChange)}
|
||||
required
|
||||
|
@ -99,12 +95,14 @@ export class Setup extends Component<any, State> {
|
|||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">
|
||||
<T i18nKey="email">#</T>
|
||||
<label class="col-sm-2 col-form-label" htmlFor="email">
|
||||
{i18n.t('email')}
|
||||
</label>
|
||||
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
class="form-control"
|
||||
placeholder={i18n.t('optional')}
|
||||
value={this.state.userForm.email}
|
||||
|
@ -114,12 +112,13 @@ export class Setup extends Component<any, State> {
|
|||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">
|
||||
<T i18nKey="password">#</T>
|
||||
<label class="col-sm-2 col-form-label" htmlFor="password">
|
||||
{i18n.t('password')}
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="password"
|
||||
id="password"
|
||||
value={this.state.userForm.password}
|
||||
onInput={linkEvent(this, this.handleRegisterPasswordChange)}
|
||||
class="form-control"
|
||||
|
@ -128,12 +127,13 @@ export class Setup extends Component<any, State> {
|
|||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">
|
||||
<T i18nKey="verify_password">#</T>
|
||||
<label class="col-sm-2 col-form-label" htmlFor="verify-password">
|
||||
{i18n.t('verify_password')}
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="password"
|
||||
id="verify-password"
|
||||
value={this.state.userForm.password_verify}
|
||||
onInput={linkEvent(this, this.handleRegisterPasswordVerifyChange)}
|
||||
class="form-control"
|
||||
|
|
69
ui/src/components/sidebar.tsx
vendored
69
ui/src/components/sidebar.tsx
vendored
|
@ -16,12 +16,12 @@ import {
|
|||
} from '../utils';
|
||||
import { CommunityForm } from './community-form';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
interface SidebarProps {
|
||||
community: Community;
|
||||
moderators: Array<CommunityUser>;
|
||||
admins: Array<UserView>;
|
||||
online: number;
|
||||
}
|
||||
|
||||
interface SidebarState {
|
||||
|
@ -72,12 +72,12 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
|||
<span>{community.title}</span>
|
||||
{community.removed && (
|
||||
<small className="ml-2 text-muted font-italic">
|
||||
<T i18nKey="removed">#</T>
|
||||
{i18n.t('removed')}
|
||||
</small>
|
||||
)}
|
||||
{community.deleted && (
|
||||
<small className="ml-2 text-muted font-italic">
|
||||
<T i18nKey="deleted">#</T>
|
||||
{i18n.t('deleted')}
|
||||
</small>
|
||||
)}
|
||||
</h5>
|
||||
|
@ -92,7 +92,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
|||
class="pointer"
|
||||
onClick={linkEvent(this, this.handleEditClick)}
|
||||
>
|
||||
<T i18nKey="edit">#</T>
|
||||
{i18n.t('edit')}
|
||||
</span>
|
||||
</li>
|
||||
{this.amCreator && (
|
||||
|
@ -116,14 +116,14 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
|||
class="pointer"
|
||||
onClick={linkEvent(this, this.handleModRemoveShow)}
|
||||
>
|
||||
<T i18nKey="remove">#</T>
|
||||
{i18n.t('remove')}
|
||||
</span>
|
||||
) : (
|
||||
<span
|
||||
class="pointer"
|
||||
onClick={linkEvent(this, this.handleModRemoveSubmit)}
|
||||
>
|
||||
<T i18nKey="restore">#</T>
|
||||
{i18n.t('restore')}
|
||||
</span>
|
||||
)}
|
||||
</li>
|
||||
|
@ -132,11 +132,12 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
|||
{this.state.showRemoveDialog && (
|
||||
<form onSubmit={linkEvent(this, this.handleModRemoveSubmit)}>
|
||||
<div class="form-group row">
|
||||
<label class="col-form-label">
|
||||
<T i18nKey="reason">#</T>
|
||||
<label class="col-form-label" htmlFor="remove-reason">
|
||||
{i18n.t('reason')}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="remove-reason"
|
||||
class="form-control mr-2"
|
||||
placeholder={i18n.t('optional')}
|
||||
value={this.state.removeReason}
|
||||
|
@ -150,47 +151,41 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
|||
{/* </div> */}
|
||||
<div class="form-group row">
|
||||
<button type="submit" class="btn btn-secondary">
|
||||
<T i18nKey="remove_community">#</T>
|
||||
{i18n.t('remove_community')}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
<ul class="my-1 list-inline">
|
||||
<li className="list-inline-item badge badge-secondary">
|
||||
{i18n.t('number_online', { count: this.props.online })}
|
||||
</li>
|
||||
<li className="list-inline-item badge badge-secondary">
|
||||
{i18n.t('number_of_subscribers', {
|
||||
count: community.number_of_subscribers,
|
||||
})}
|
||||
</li>
|
||||
<li className="list-inline-item badge badge-secondary">
|
||||
{i18n.t('number_of_posts', {
|
||||
count: community.number_of_posts,
|
||||
})}
|
||||
</li>
|
||||
<li className="list-inline-item badge badge-secondary">
|
||||
{i18n.t('number_of_comments', {
|
||||
count: community.number_of_comments,
|
||||
})}
|
||||
</li>
|
||||
<li className="list-inline-item">
|
||||
<Link className="badge badge-secondary" to="/communities">
|
||||
{community.category_name}
|
||||
</Link>
|
||||
</li>
|
||||
<li className="list-inline-item badge badge-secondary">
|
||||
<T
|
||||
i18nKey="number_of_subscribers"
|
||||
interpolation={{ count: community.number_of_subscribers }}
|
||||
>
|
||||
#
|
||||
</T>
|
||||
</li>
|
||||
<li className="list-inline-item badge badge-secondary">
|
||||
<T
|
||||
i18nKey="number_of_posts"
|
||||
interpolation={{ count: community.number_of_posts }}
|
||||
>
|
||||
#
|
||||
</T>
|
||||
</li>
|
||||
<li className="list-inline-item badge badge-secondary">
|
||||
<T
|
||||
i18nKey="number_of_comments"
|
||||
interpolation={{ count: community.number_of_comments }}
|
||||
>
|
||||
#
|
||||
</T>
|
||||
</li>
|
||||
<li className="list-inline-item">
|
||||
<Link
|
||||
className="badge badge-secondary"
|
||||
to={`/modlog/community/${this.props.community.id}`}
|
||||
>
|
||||
<T i18nKey="modlog">#</T>
|
||||
{i18n.t('modlog')}
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -218,7 +213,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
|||
'no-click'}`}
|
||||
to={`/create_post?community=${community.name}`}
|
||||
>
|
||||
<T i18nKey="create_a_post">#</T>
|
||||
{i18n.t('create_a_post')}
|
||||
</Link>
|
||||
<div>
|
||||
{community.subscribed ? (
|
||||
|
@ -226,14 +221,14 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
|||
class="btn btn-sm btn-secondary btn-block"
|
||||
onClick={linkEvent(community.id, this.handleUnsubscribe)}
|
||||
>
|
||||
<T i18nKey="unsubscribe">#</T>
|
||||
{i18n.t('unsubscribe')}
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
class="btn btn-sm btn-secondary btn-block"
|
||||
onClick={linkEvent(community.id, this.handleSubscribe)}
|
||||
>
|
||||
<T i18nKey="subscribe">#</T>
|
||||
{i18n.t('subscribe')}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
|
30
ui/src/components/site-form.tsx
vendored
30
ui/src/components/site-form.tsx
vendored
|
@ -5,7 +5,6 @@ import { capitalizeFirstLetter, randomStr, setupTribute } from '../utils';
|
|||
import autosize from 'autosize';
|
||||
import Tribute from 'tributejs/src/Tribute.js';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
interface SiteFormProps {
|
||||
site?: Site; // If a site is given, that means this is an edit
|
||||
|
@ -67,12 +66,13 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
|||
: capitalizeFirstLetter(i18n.t('name'))
|
||||
} ${i18n.t('your_site')}`}</h5>
|
||||
<div class="form-group row">
|
||||
<label class="col-12 col-form-label">
|
||||
<T i18nKey="name">#</T>
|
||||
<label class="col-12 col-form-label" htmlFor="create-site-name">
|
||||
{i18n.t('name')}
|
||||
</label>
|
||||
<div class="col-12">
|
||||
<input
|
||||
type="text"
|
||||
id="create-site-name"
|
||||
class="form-control"
|
||||
value={this.state.siteForm.name}
|
||||
onInput={linkEvent(this, this.handleSiteNameChange)}
|
||||
|
@ -83,8 +83,8 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
|||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-12 col-form-label">
|
||||
<T i18nKey="sidebar">#</T>
|
||||
<label class="col-12 col-form-label" htmlFor={this.id}>
|
||||
{i18n.t('sidebar')}
|
||||
</label>
|
||||
<div class="col-12">
|
||||
<textarea
|
||||
|
@ -102,12 +102,13 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
|||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
id="create-site-downvotes"
|
||||
type="checkbox"
|
||||
checked={this.state.siteForm.enable_downvotes}
|
||||
onChange={linkEvent(this, this.handleSiteEnableDownvotesChange)}
|
||||
/>
|
||||
<label class="form-check-label">
|
||||
<T i18nKey="enable_downvotes">#</T>
|
||||
<label class="form-check-label" htmlFor="create-site-downvotes">
|
||||
{i18n.t('enable_downvotes')}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -117,12 +118,13 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
|||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
id="create-site-enable-nsfw"
|
||||
type="checkbox"
|
||||
checked={this.state.siteForm.enable_nsfw}
|
||||
onChange={linkEvent(this, this.handleSiteEnableNsfwChange)}
|
||||
/>
|
||||
<label class="form-check-label">
|
||||
<T i18nKey="enable_nsfw">#</T>
|
||||
<label class="form-check-label" htmlFor="create-site-enable-nsfw">
|
||||
{i18n.t('enable_nsfw')}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -132,6 +134,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
|||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
id="create-site-open-registration"
|
||||
type="checkbox"
|
||||
checked={this.state.siteForm.open_registration}
|
||||
onChange={linkEvent(
|
||||
|
@ -139,8 +142,11 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
|||
this.handleSiteOpenRegistrationChange
|
||||
)}
|
||||
/>
|
||||
<label class="form-check-label">
|
||||
<T i18nKey="open_registration">#</T>
|
||||
<label
|
||||
class="form-check-label"
|
||||
htmlFor="create-site-open-registration"
|
||||
>
|
||||
{i18n.t('open_registration')}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -164,7 +170,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
|||
class="btn btn-secondary"
|
||||
onClick={linkEvent(this, this.handleCancel)}
|
||||
>
|
||||
<T i18nKey="cancel">#</T>
|
||||
{i18n.t('cancel')}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
|
35
ui/src/components/sort-select.tsx
vendored
35
ui/src/components/sort-select.tsx
vendored
|
@ -1,7 +1,6 @@
|
|||
import { Component, linkEvent } from 'inferno';
|
||||
import { SortType } from '../interfaces';
|
||||
|
||||
import { T } from 'inferno-i18next';
|
||||
import { i18n } from '../i18next';
|
||||
|
||||
interface SortSelectProps {
|
||||
sort: SortType;
|
||||
|
@ -30,33 +29,17 @@ export class SortSelect extends Component<SortSelectProps, SortSelectState> {
|
|||
onChange={linkEvent(this, this.handleSortChange)}
|
||||
class="custom-select custom-select-sm w-auto"
|
||||
>
|
||||
<option disabled>
|
||||
<T i18nKey="sort_type">#</T>
|
||||
</option>
|
||||
<option disabled>{i18n.t('sort_type')}</option>
|
||||
{!this.props.hideHot && (
|
||||
<option value={SortType.Hot}>
|
||||
<T i18nKey="hot">#</T>
|
||||
</option>
|
||||
<option value={SortType.Hot}>{i18n.t('hot')}</option>
|
||||
)}
|
||||
<option value={SortType.New}>
|
||||
<T i18nKey="new">#</T>
|
||||
</option>
|
||||
<option value={SortType.New}>{i18n.t('new')}</option>
|
||||
<option disabled>─────</option>
|
||||
<option value={SortType.TopDay}>
|
||||
<T i18nKey="top_day">#</T>
|
||||
</option>
|
||||
<option value={SortType.TopWeek}>
|
||||
<T i18nKey="week">#</T>
|
||||
</option>
|
||||
<option value={SortType.TopMonth}>
|
||||
<T i18nKey="month">#</T>
|
||||
</option>
|
||||
<option value={SortType.TopYear}>
|
||||
<T i18nKey="year">#</T>
|
||||
</option>
|
||||
<option value={SortType.TopAll}>
|
||||
<T i18nKey="all">#</T>
|
||||
</option>
|
||||
<option value={SortType.TopDay}>{i18n.t('top_day')}</option>
|
||||
<option value={SortType.TopWeek}>{i18n.t('week')}</option>
|
||||
<option value={SortType.TopMonth}>{i18n.t('month')}</option>
|
||||
<option value={SortType.TopYear}>{i18n.t('year')}</option>
|
||||
<option value={SortType.TopAll}>{i18n.t('all')}</option>
|
||||
</select>
|
||||
);
|
||||
}
|
||||
|
|
37
ui/src/components/sponsors.tsx
vendored
37
ui/src/components/sponsors.tsx
vendored
|
@ -3,7 +3,12 @@ import { WebSocketService } from '../services';
|
|||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
let general = ['Andre Vallestero', 'riccardo', 'NotTooHighToHack'];
|
||||
let general = [
|
||||
'Nathan J. Goode',
|
||||
'Andre Vallestero',
|
||||
'riccardo',
|
||||
'NotTooHighToHack',
|
||||
];
|
||||
let highlighted = ['Alex Benishek'];
|
||||
// let silver = [];
|
||||
// let gold = [];
|
||||
|
@ -36,16 +41,14 @@ export class Sponsors extends Component<any, any> {
|
|||
topMessage() {
|
||||
return (
|
||||
<div>
|
||||
<h5>
|
||||
<T i18nKey="donate_to_lemmy">#</T>
|
||||
</h5>
|
||||
<h5>{i18n.t('donate_to_lemmy')}</h5>
|
||||
<p>
|
||||
<T i18nKey="sponsor_message">
|
||||
#<a href="https://github.com/dessalines/lemmy">#</a>
|
||||
</T>
|
||||
</p>
|
||||
<a class="btn btn-secondary" href="https://www.patreon.com/dessalines">
|
||||
<T i18nKey="support_on_patreon">#</T>
|
||||
{i18n.t('support_on_patreon')}
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
|
@ -53,12 +56,8 @@ export class Sponsors extends Component<any, any> {
|
|||
sponsors() {
|
||||
return (
|
||||
<div class="container">
|
||||
<h5>
|
||||
<T i18nKey="sponsors">#</T>
|
||||
</h5>
|
||||
<p>
|
||||
<T i18nKey="general_sponsors">#</T>
|
||||
</p>
|
||||
<h5>{i18n.t('sponsors')}</h5>
|
||||
<p>{i18n.t('general_sponsors')}</p>
|
||||
<div class="row card-columns">
|
||||
{highlighted.map(s => (
|
||||
<div class="card bg-primary col-12 col-md-2 font-weight-bold">
|
||||
|
@ -78,32 +77,24 @@ export class Sponsors extends Component<any, any> {
|
|||
bitcoin() {
|
||||
return (
|
||||
<div>
|
||||
<h5>
|
||||
<T i18nKey="crypto">#</T>
|
||||
</h5>
|
||||
<h5>{i18n.t('crypto')}</h5>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover text-center">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<T i18nKey="bitcoin">#</T>
|
||||
</td>
|
||||
<td>{i18n.t('bitcoin')}</td>
|
||||
<td>
|
||||
<code>1Hefs7miXS5ff5Ck5xvmjKjXf5242KzRtK</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<T i18nKey="ethereum">#</T>
|
||||
</td>
|
||||
<td>{i18n.t('ethereum')}</td>
|
||||
<td>
|
||||
<code>0x400c96c96acbC6E7B3B43B1dc1BB446540a88A01</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<T i18nKey="monero">#</T>
|
||||
</td>
|
||||
<td>{i18n.t('monero')}</td>
|
||||
<td>
|
||||
<code>
|
||||
41taVyY6e1xApqKyMVDRVxJ76sPkfZhALLTjRvVKpaAh2pBd4wv9RgYj1tSPrx8wc6iE1uWUfjtQdTmTy2FGMeChGVKPQuV
|
||||
|
|
159
ui/src/components/user.tsx
vendored
159
ui/src/components/user.tsx
vendored
|
@ -18,7 +18,7 @@ import {
|
|||
BanUserResponse,
|
||||
AddAdminResponse,
|
||||
DeleteAccountForm,
|
||||
CreatePostLikeResponse,
|
||||
PostResponse,
|
||||
WebSocketJsonResponse,
|
||||
} from '../interfaces';
|
||||
import { WebSocketService, UserService } from '../services';
|
||||
|
@ -39,7 +39,6 @@ import { ListingTypeSelect } from './listing-type-select';
|
|||
import { CommentNodes } from './comment-nodes';
|
||||
import { MomentTime } from './moment-time';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
enum View {
|
||||
Overview,
|
||||
|
@ -245,21 +244,11 @@ export class User extends Component<any, UserState> {
|
|||
onChange={linkEvent(this, this.handleViewChange)}
|
||||
class="custom-select custom-select-sm w-auto"
|
||||
>
|
||||
<option disabled>
|
||||
<T i18nKey="view">#</T>
|
||||
</option>
|
||||
<option value={View.Overview}>
|
||||
<T i18nKey="overview">#</T>
|
||||
</option>
|
||||
<option value={View.Comments}>
|
||||
<T i18nKey="comments">#</T>
|
||||
</option>
|
||||
<option value={View.Posts}>
|
||||
<T i18nKey="posts">#</T>
|
||||
</option>
|
||||
<option value={View.Saved}>
|
||||
<T i18nKey="saved">#</T>
|
||||
</option>
|
||||
<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
|
||||
|
@ -359,7 +348,7 @@ export class User extends Component<any, UserState> {
|
|||
<li className="list-inline-item">{user.name}</li>
|
||||
{user.banned && (
|
||||
<li className="list-inline-item badge badge-danger">
|
||||
<T i18nKey="banned">#</T>
|
||||
{i18n.t('banned')}
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
|
@ -371,38 +360,20 @@ export class User extends Component<any, UserState> {
|
|||
<table class="table table-bordered table-sm mt-2 mb-0">
|
||||
<tr>
|
||||
<td>
|
||||
<T
|
||||
i18nKey="number_of_points"
|
||||
interpolation={{ count: user.post_score }}
|
||||
>
|
||||
#
|
||||
</T>
|
||||
{i18n.t('number_of_points', { count: user.post_score })}
|
||||
</td>
|
||||
<td>
|
||||
<T
|
||||
i18nKey="number_of_posts"
|
||||
interpolation={{ count: user.number_of_posts }}
|
||||
>
|
||||
#
|
||||
</T>
|
||||
{i18n.t('number_of_posts', { count: user.number_of_posts })}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<T
|
||||
i18nKey="number_of_points"
|
||||
interpolation={{ count: user.comment_score }}
|
||||
>
|
||||
#
|
||||
</T>
|
||||
{i18n.t('number_of_points', { count: user.comment_score })}
|
||||
</td>
|
||||
<td>
|
||||
<T
|
||||
i18nKey="number_of_comments"
|
||||
interpolation={{ count: user.number_of_comments }}
|
||||
>
|
||||
#
|
||||
</T>
|
||||
{i18n.t('number_of_comments', {
|
||||
count: user.number_of_comments,
|
||||
})}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
@ -412,7 +383,7 @@ export class User extends Component<any, UserState> {
|
|||
class="btn btn-block btn-secondary mt-3"
|
||||
onClick={linkEvent(this, this.handleLogoutClick)}
|
||||
>
|
||||
<T i18nKey="logout">#</T>
|
||||
{i18n.t('logout')}
|
||||
</button>
|
||||
) : (
|
||||
<>
|
||||
|
@ -443,14 +414,10 @@ export class User extends Component<any, UserState> {
|
|||
<div>
|
||||
<div class="card border-secondary mb-3">
|
||||
<div class="card-body">
|
||||
<h5>
|
||||
<T i18nKey="settings">#</T>
|
||||
</h5>
|
||||
<h5>{i18n.t('settings')}</h5>
|
||||
<form onSubmit={linkEvent(this, this.handleUserSettingsSubmit)}>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<T i18nKey="avatar">#</T>
|
||||
</label>
|
||||
<label>{i18n.t('avatar')}</label>
|
||||
<form class="d-inline">
|
||||
<label
|
||||
htmlFor="file-upload"
|
||||
|
@ -458,7 +425,7 @@ export class User extends Component<any, UserState> {
|
|||
>
|
||||
{!this.state.userSettingsForm.avatar ? (
|
||||
<span class="btn btn-sm btn-secondary">
|
||||
<T i18nKey="upload_avatar">#</T>
|
||||
{i18n.t('upload_avatar')}
|
||||
</span>
|
||||
) : (
|
||||
<img
|
||||
|
@ -481,20 +448,14 @@ export class User extends Component<any, UserState> {
|
|||
</form>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<T i18nKey="language">#</T>
|
||||
</label>
|
||||
<label>{i18n.t('language')}</label>
|
||||
<select
|
||||
value={this.state.userSettingsForm.lang}
|
||||
onChange={linkEvent(this, this.handleUserSettingsLangChange)}
|
||||
class="ml-2 custom-select custom-select-sm w-auto"
|
||||
>
|
||||
<option disabled>
|
||||
<T i18nKey="language">#</T>
|
||||
</option>
|
||||
<option value="browser">
|
||||
<T i18nKey="browser_default">#</T>
|
||||
</option>
|
||||
<option disabled>{i18n.t('language')}</option>
|
||||
<option value="browser">{i18n.t('browser_default')}</option>
|
||||
<option disabled>──</option>
|
||||
{languages.map(lang => (
|
||||
<option value={lang.code}>{lang.name}</option>
|
||||
|
@ -502,17 +463,13 @@ export class User extends Component<any, UserState> {
|
|||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<T i18nKey="theme">#</T>
|
||||
</label>
|
||||
<label>{i18n.t('theme')}</label>
|
||||
<select
|
||||
value={this.state.userSettingsForm.theme}
|
||||
onChange={linkEvent(this, this.handleUserSettingsThemeChange)}
|
||||
class="ml-2 custom-select custom-select-sm w-auto"
|
||||
>
|
||||
<option disabled>
|
||||
<T i18nKey="theme">#</T>
|
||||
</option>
|
||||
<option disabled>{i18n.t('theme')}</option>
|
||||
{themes.map(theme => (
|
||||
<option value={theme}>{theme}</option>
|
||||
))}
|
||||
|
@ -520,9 +477,7 @@ export class User extends Component<any, UserState> {
|
|||
</div>
|
||||
<form className="form-group">
|
||||
<label>
|
||||
<T i18nKey="sort_type" class="mr-2">
|
||||
#
|
||||
</T>
|
||||
<div class="mr-2">{i18n.t('sort_type')}</div>
|
||||
</label>
|
||||
<ListingTypeSelect
|
||||
type_={this.state.userSettingsForm.default_listing_type}
|
||||
|
@ -531,9 +486,7 @@ export class User extends Component<any, UserState> {
|
|||
</form>
|
||||
<form className="form-group">
|
||||
<label>
|
||||
<T i18nKey="type" class="mr-2">
|
||||
#
|
||||
</T>
|
||||
<div class="mr-2">{i18n.t('type')}</div>
|
||||
</label>
|
||||
<SortSelect
|
||||
sort={this.state.userSettingsForm.default_sort_type}
|
||||
|
@ -541,12 +494,13 @@ export class User extends Component<any, UserState> {
|
|||
/>
|
||||
</form>
|
||||
<div class="form-group row">
|
||||
<label class="col-lg-3 col-form-label">
|
||||
<T i18nKey="email">#</T>
|
||||
<label class="col-lg-3 col-form-label" htmlFor="user-email">
|
||||
{i18n.t('email')}
|
||||
</label>
|
||||
<div class="col-lg-9">
|
||||
<input
|
||||
type="email"
|
||||
id="user-email"
|
||||
class="form-control"
|
||||
placeholder={i18n.t('optional')}
|
||||
value={this.state.userSettingsForm.email}
|
||||
|
@ -579,12 +533,13 @@ export class User extends Component<any, UserState> {
|
|||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-lg-5 col-form-label">
|
||||
<T i18nKey="new_password">#</T>
|
||||
<label class="col-lg-5 col-form-label" htmlFor="user-password">
|
||||
{i18n.t('new_password')}
|
||||
</label>
|
||||
<div class="col-lg-7">
|
||||
<input
|
||||
type="password"
|
||||
id="user-password"
|
||||
class="form-control"
|
||||
value={this.state.userSettingsForm.new_password}
|
||||
onInput={linkEvent(
|
||||
|
@ -595,12 +550,16 @@ export class User extends Component<any, UserState> {
|
|||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-lg-5 col-form-label">
|
||||
<T i18nKey="verify_password">#</T>
|
||||
<label
|
||||
class="col-lg-5 col-form-label"
|
||||
htmlFor="user-verify-password"
|
||||
>
|
||||
{i18n.t('verify_password')}
|
||||
</label>
|
||||
<div class="col-lg-7">
|
||||
<input
|
||||
type="password"
|
||||
id="user-verify-password"
|
||||
class="form-control"
|
||||
value={this.state.userSettingsForm.new_password_verify}
|
||||
onInput={linkEvent(
|
||||
|
@ -611,12 +570,16 @@ export class User extends Component<any, UserState> {
|
|||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-lg-5 col-form-label">
|
||||
<T i18nKey="old_password">#</T>
|
||||
<label
|
||||
class="col-lg-5 col-form-label"
|
||||
htmlFor="user-old-password"
|
||||
>
|
||||
{i18n.t('old_password')}
|
||||
</label>
|
||||
<div class="col-lg-7">
|
||||
<input
|
||||
type="password"
|
||||
id="user-old-password"
|
||||
class="form-control"
|
||||
value={this.state.userSettingsForm.old_password}
|
||||
onInput={linkEvent(
|
||||
|
@ -631,6 +594,7 @@ export class User extends Component<any, UserState> {
|
|||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
id="user-show-nsfw"
|
||||
type="checkbox"
|
||||
checked={this.state.userSettingsForm.show_nsfw}
|
||||
onChange={linkEvent(
|
||||
|
@ -638,8 +602,8 @@ export class User extends Component<any, UserState> {
|
|||
this.handleUserSettingsShowNsfwChange
|
||||
)}
|
||||
/>
|
||||
<label class="form-check-label">
|
||||
<T i18nKey="show_nsfw">#</T>
|
||||
<label class="form-check-label" htmlFor="user-show-nsfw">
|
||||
{i18n.t('show_nsfw')}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -648,6 +612,7 @@ export class User extends Component<any, UserState> {
|
|||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
id="user-show-avatars"
|
||||
type="checkbox"
|
||||
checked={this.state.userSettingsForm.show_avatars}
|
||||
onChange={linkEvent(
|
||||
|
@ -655,8 +620,8 @@ export class User extends Component<any, UserState> {
|
|||
this.handleUserSettingsShowAvatarsChange
|
||||
)}
|
||||
/>
|
||||
<label class="form-check-label">
|
||||
<T i18nKey="show_avatars">#</T>
|
||||
<label class="form-check-label" htmlFor="user-show-avatars">
|
||||
{i18n.t('show_avatars')}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -664,6 +629,7 @@ export class User extends Component<any, UserState> {
|
|||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
id="user-send-notifications-to-email"
|
||||
type="checkbox"
|
||||
disabled={!this.state.user.email}
|
||||
checked={
|
||||
|
@ -674,8 +640,11 @@ export class User extends Component<any, UserState> {
|
|||
this.handleUserSettingsSendNotificationsToEmailChange
|
||||
)}
|
||||
/>
|
||||
<label class="form-check-label">
|
||||
<T i18nKey="send_notifications_to_email">#</T>
|
||||
<label
|
||||
class="form-check-label"
|
||||
htmlFor="user-send-notifications-to-email"
|
||||
>
|
||||
{i18n.t('send_notifications_to_email')}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -699,12 +668,12 @@ export class User extends Component<any, UserState> {
|
|||
this.handleDeleteAccountShowConfirmToggle
|
||||
)}
|
||||
>
|
||||
<T i18nKey="delete_account">#</T>
|
||||
{i18n.t('delete_account')}
|
||||
</button>
|
||||
{this.state.deleteAccountShowConfirm && (
|
||||
<>
|
||||
<div class="my-2 alert alert-danger" role="alert">
|
||||
<T i18nKey="delete_account_confirm">#</T>
|
||||
{i18n.t('delete_account_confirm')}
|
||||
</div>
|
||||
<input
|
||||
type="password"
|
||||
|
@ -735,7 +704,7 @@ export class User extends Component<any, UserState> {
|
|||
this.handleDeleteAccountShowConfirmToggle
|
||||
)}
|
||||
>
|
||||
<T i18nKey="cancel">#</T>
|
||||
{i18n.t('cancel')}
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
|
@ -753,9 +722,7 @@ export class User extends Component<any, UserState> {
|
|||
{this.state.moderates.length > 0 && (
|
||||
<div class="card border-secondary mb-3">
|
||||
<div class="card-body">
|
||||
<h5>
|
||||
<T i18nKey="moderates">#</T>
|
||||
</h5>
|
||||
<h5>{i18n.t('moderates')}</h5>
|
||||
<ul class="list-unstyled mb-0">
|
||||
{this.state.moderates.map(community => (
|
||||
<li>
|
||||
|
@ -778,9 +745,7 @@ export class User extends Component<any, UserState> {
|
|||
{this.state.follows.length > 0 && (
|
||||
<div class="card border-secondary mb-3">
|
||||
<div class="card-body">
|
||||
<h5>
|
||||
<T i18nKey="subscribed">#</T>
|
||||
</h5>
|
||||
<h5>{i18n.t('subscribed')}</h5>
|
||||
<ul class="list-unstyled mb-0">
|
||||
{this.state.follows.map(community => (
|
||||
<li>
|
||||
|
@ -805,14 +770,14 @@ export class User extends Component<any, UserState> {
|
|||
class="btn btn-sm btn-secondary mr-1"
|
||||
onClick={linkEvent(this, this.prevPage)}
|
||||
>
|
||||
<T i18nKey="prev">#</T>
|
||||
{i18n.t('prev')}
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
class="btn btn-sm btn-secondary"
|
||||
onClick={linkEvent(this, this.nextPage)}
|
||||
>
|
||||
<T i18nKey="next">#</T>
|
||||
{i18n.t('next')}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
@ -1090,7 +1055,7 @@ export class User extends Component<any, UserState> {
|
|||
if (data.comment.my_vote !== null) found.my_vote = data.comment.my_vote;
|
||||
this.setState(this.state);
|
||||
} else if (res.op == UserOperation.CreatePostLike) {
|
||||
let data = res.data as CreatePostLikeResponse;
|
||||
let data = res.data as PostResponse;
|
||||
let found = this.state.posts.find(c => c.id == data.post.id);
|
||||
found.my_vote = data.post.my_vote;
|
||||
found.score = data.post.score;
|
||||
|
|
8
ui/src/env.ts
vendored
8
ui/src/env.ts
vendored
|
@ -1,5 +1,9 @@
|
|||
const host = `${window.location.hostname}`;
|
||||
const port = `${window.location.port == '4444' ? '8536' : window.location.port}`;
|
||||
const port = `${
|
||||
window.location.port == '4444' ? '8536' : window.location.port
|
||||
}`;
|
||||
const endpoint = `${host}:${port}`;
|
||||
|
||||
export const wsUri = `${window.location.protocol == 'https:' ? 'wss://' : 'ws://'}${endpoint}/api/v1/ws`;
|
||||
export const wsUri = `${
|
||||
window.location.protocol == 'https:' ? 'wss://' : 'ws://'
|
||||
}${endpoint}/api/v1/ws`;
|
||||
|
|
2
ui/src/i18next.ts
vendored
2
ui/src/i18next.ts
vendored
|
@ -12,6 +12,7 @@ import { nl } from './translations/nl';
|
|||
import { it } from './translations/it';
|
||||
import { fi } from './translations/fi';
|
||||
import { ca } from './translations/ca';
|
||||
import { fa } from './translations/fa';
|
||||
|
||||
// https://github.com/nimbusec-oss/inferno-i18next/blob/master/tests/T.test.js#L66
|
||||
const resources = {
|
||||
|
@ -27,6 +28,7 @@ const resources = {
|
|||
it,
|
||||
fi,
|
||||
ca,
|
||||
fa,
|
||||
};
|
||||
|
||||
function format(value: any, format: any, lng: any): any {
|
||||
|
|
18
ui/src/interfaces.ts
vendored
18
ui/src/interfaces.ts
vendored
|
@ -41,6 +41,7 @@ export enum UserOperation {
|
|||
CreatePrivateMessage,
|
||||
EditPrivateMessage,
|
||||
GetPrivateMessages,
|
||||
UserJoin,
|
||||
}
|
||||
|
||||
export enum CommentSortType {
|
||||
|
@ -538,6 +539,7 @@ export interface GetCommunityResponse {
|
|||
community: Community;
|
||||
moderators: Array<CommunityUser>;
|
||||
admins: Array<UserView>;
|
||||
online: number;
|
||||
}
|
||||
|
||||
export interface CommunityResponse {
|
||||
|
@ -594,6 +596,7 @@ export interface GetPostResponse {
|
|||
community: Community;
|
||||
moderators: Array<CommunityUser>;
|
||||
admins: Array<UserView>;
|
||||
online: number;
|
||||
}
|
||||
|
||||
export interface SavePostForm {
|
||||
|
@ -627,6 +630,7 @@ export interface SaveCommentForm {
|
|||
|
||||
export interface CommentResponse {
|
||||
comment: Comment;
|
||||
recipient_ids: Array<number>;
|
||||
}
|
||||
|
||||
export interface CommentLikeForm {
|
||||
|
@ -660,10 +664,6 @@ export interface CreatePostLikeForm {
|
|||
auth?: string;
|
||||
}
|
||||
|
||||
export interface CreatePostLikeResponse {
|
||||
post: Post;
|
||||
}
|
||||
|
||||
export interface SiteForm {
|
||||
name: string;
|
||||
description?: string;
|
||||
|
@ -775,6 +775,14 @@ export interface PrivateMessageResponse {
|
|||
message: PrivateMessage;
|
||||
}
|
||||
|
||||
export interface UserJoinForm {
|
||||
auth: string;
|
||||
}
|
||||
|
||||
export interface UserJoinResponse {
|
||||
user_id: number;
|
||||
}
|
||||
|
||||
export type MessageType =
|
||||
| EditPrivateMessageForm
|
||||
| LoginForm
|
||||
|
@ -819,7 +827,7 @@ type ResponseType =
|
|||
| GetFollowedCommunitiesResponse
|
||||
| ListCommunitiesResponse
|
||||
| GetPostsResponse
|
||||
| CreatePostLikeResponse
|
||||
| PostResponse
|
||||
| GetRepliesResponse
|
||||
| GetUserMentionsResponse
|
||||
| ListCategoriesResponse
|
||||
|
|
148
ui/src/services/WebSocketService.ts
vendored
148
ui/src/services/WebSocketService.ts
vendored
|
@ -38,281 +38,268 @@ import {
|
|||
PrivateMessageForm,
|
||||
EditPrivateMessageForm,
|
||||
GetPrivateMessagesForm,
|
||||
UserJoinForm,
|
||||
MessageType,
|
||||
} from '../interfaces';
|
||||
import { webSocket } from 'rxjs/webSocket';
|
||||
import { Subject } from 'rxjs';
|
||||
import { retryWhen, delay } from 'rxjs/operators';
|
||||
import { UserService } from './';
|
||||
import { i18n } from '../i18next';
|
||||
import { toast } from '../utils';
|
||||
import { Observable } from 'rxjs';
|
||||
import { share } from 'rxjs/operators';
|
||||
import ReconnectingWebSocket from 'reconnecting-websocket';
|
||||
|
||||
export class WebSocketService {
|
||||
private static _instance: WebSocketService;
|
||||
public subject: Subject<any>;
|
||||
public ws: ReconnectingWebSocket;
|
||||
public subject: Observable<any>;
|
||||
|
||||
public site: Site;
|
||||
public admins: Array<UserView>;
|
||||
public banned: Array<UserView>;
|
||||
|
||||
private constructor() {
|
||||
this.subject = webSocket(wsUri);
|
||||
|
||||
// Necessary to not keep reconnecting
|
||||
this.subject
|
||||
.pipe(
|
||||
retryWhen(errors =>
|
||||
errors.pipe(
|
||||
delay(1000)
|
||||
// take(999)
|
||||
)
|
||||
)
|
||||
)
|
||||
.subscribe();
|
||||
|
||||
this.ws = new ReconnectingWebSocket(wsUri);
|
||||
this.ws.onopen = () => {
|
||||
console.log(`Connected to ${wsUri}`);
|
||||
if (UserService.Instance.user) {
|
||||
this.userJoin();
|
||||
}
|
||||
};
|
||||
|
||||
this.subject = Observable.create((obs: any) => {
|
||||
this.ws.onmessage = e => {
|
||||
obs.next(JSON.parse(e.data));
|
||||
};
|
||||
}).pipe(share());
|
||||
}
|
||||
|
||||
public static get Instance() {
|
||||
return this._instance || (this._instance = new this());
|
||||
}
|
||||
|
||||
public userJoin() {
|
||||
let form: UserJoinForm = { auth: UserService.Instance.auth };
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.UserJoin, form));
|
||||
}
|
||||
|
||||
public login(loginForm: LoginForm) {
|
||||
this.subject.next(this.wsSendWrapper(UserOperation.Login, loginForm));
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.Login, loginForm));
|
||||
}
|
||||
|
||||
public register(registerForm: RegisterForm) {
|
||||
this.subject.next(this.wsSendWrapper(UserOperation.Register, registerForm));
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.Register, registerForm));
|
||||
}
|
||||
|
||||
public createCommunity(communityForm: CommunityForm) {
|
||||
this.setAuth(communityForm);
|
||||
this.subject.next(
|
||||
this.ws.send(
|
||||
this.wsSendWrapper(UserOperation.CreateCommunity, communityForm)
|
||||
);
|
||||
}
|
||||
|
||||
public editCommunity(communityForm: CommunityForm) {
|
||||
this.setAuth(communityForm);
|
||||
this.subject.next(
|
||||
this.ws.send(
|
||||
this.wsSendWrapper(UserOperation.EditCommunity, communityForm)
|
||||
);
|
||||
}
|
||||
|
||||
public followCommunity(followCommunityForm: FollowCommunityForm) {
|
||||
this.setAuth(followCommunityForm);
|
||||
this.subject.next(
|
||||
this.ws.send(
|
||||
this.wsSendWrapper(UserOperation.FollowCommunity, followCommunityForm)
|
||||
);
|
||||
}
|
||||
|
||||
public listCommunities(form: ListCommunitiesForm) {
|
||||
this.setAuth(form, false);
|
||||
this.subject.next(this.wsSendWrapper(UserOperation.ListCommunities, form));
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.ListCommunities, form));
|
||||
}
|
||||
|
||||
public getFollowedCommunities() {
|
||||
let form: GetFollowedCommunitiesForm = { auth: UserService.Instance.auth };
|
||||
this.subject.next(
|
||||
this.ws.send(
|
||||
this.wsSendWrapper(UserOperation.GetFollowedCommunities, form)
|
||||
);
|
||||
}
|
||||
|
||||
public listCategories() {
|
||||
this.subject.next(
|
||||
this.wsSendWrapper(UserOperation.ListCategories, undefined)
|
||||
);
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.ListCategories, {}));
|
||||
}
|
||||
|
||||
public createPost(postForm: PostForm) {
|
||||
this.setAuth(postForm);
|
||||
this.subject.next(this.wsSendWrapper(UserOperation.CreatePost, postForm));
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.CreatePost, postForm));
|
||||
}
|
||||
|
||||
public getPost(form: GetPostForm) {
|
||||
this.setAuth(form, false);
|
||||
this.subject.next(this.wsSendWrapper(UserOperation.GetPost, form));
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.GetPost, form));
|
||||
}
|
||||
|
||||
public getCommunity(form: GetCommunityForm) {
|
||||
this.setAuth(form, false);
|
||||
this.subject.next(this.wsSendWrapper(UserOperation.GetCommunity, form));
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.GetCommunity, form));
|
||||
}
|
||||
|
||||
public createComment(commentForm: CommentForm) {
|
||||
this.setAuth(commentForm);
|
||||
this.subject.next(
|
||||
this.wsSendWrapper(UserOperation.CreateComment, commentForm)
|
||||
);
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.CreateComment, commentForm));
|
||||
}
|
||||
|
||||
public editComment(commentForm: CommentForm) {
|
||||
this.setAuth(commentForm);
|
||||
this.subject.next(
|
||||
this.wsSendWrapper(UserOperation.EditComment, commentForm)
|
||||
);
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.EditComment, commentForm));
|
||||
}
|
||||
|
||||
public likeComment(form: CommentLikeForm) {
|
||||
this.setAuth(form);
|
||||
this.subject.next(
|
||||
this.wsSendWrapper(UserOperation.CreateCommentLike, form)
|
||||
);
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.CreateCommentLike, form));
|
||||
}
|
||||
|
||||
public saveComment(form: SaveCommentForm) {
|
||||
this.setAuth(form);
|
||||
this.subject.next(this.wsSendWrapper(UserOperation.SaveComment, form));
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.SaveComment, form));
|
||||
}
|
||||
|
||||
public getPosts(form: GetPostsForm) {
|
||||
this.setAuth(form, false);
|
||||
this.subject.next(this.wsSendWrapper(UserOperation.GetPosts, form));
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.GetPosts, form));
|
||||
}
|
||||
|
||||
public likePost(form: CreatePostLikeForm) {
|
||||
this.setAuth(form);
|
||||
this.subject.next(this.wsSendWrapper(UserOperation.CreatePostLike, form));
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.CreatePostLike, form));
|
||||
}
|
||||
|
||||
public editPost(postForm: PostForm) {
|
||||
this.setAuth(postForm);
|
||||
this.subject.next(this.wsSendWrapper(UserOperation.EditPost, postForm));
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.EditPost, postForm));
|
||||
}
|
||||
|
||||
public savePost(form: SavePostForm) {
|
||||
this.setAuth(form);
|
||||
this.subject.next(this.wsSendWrapper(UserOperation.SavePost, form));
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.SavePost, form));
|
||||
}
|
||||
|
||||
public banFromCommunity(form: BanFromCommunityForm) {
|
||||
this.setAuth(form);
|
||||
this.subject.next(this.wsSendWrapper(UserOperation.BanFromCommunity, form));
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.BanFromCommunity, form));
|
||||
}
|
||||
|
||||
public addModToCommunity(form: AddModToCommunityForm) {
|
||||
this.setAuth(form);
|
||||
this.subject.next(
|
||||
this.wsSendWrapper(UserOperation.AddModToCommunity, form)
|
||||
);
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.AddModToCommunity, form));
|
||||
}
|
||||
|
||||
public transferCommunity(form: TransferCommunityForm) {
|
||||
this.setAuth(form);
|
||||
this.subject.next(
|
||||
this.wsSendWrapper(UserOperation.TransferCommunity, form)
|
||||
);
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.TransferCommunity, form));
|
||||
}
|
||||
|
||||
public transferSite(form: TransferSiteForm) {
|
||||
this.setAuth(form);
|
||||
this.subject.next(this.wsSendWrapper(UserOperation.TransferSite, form));
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.TransferSite, form));
|
||||
}
|
||||
|
||||
public banUser(form: BanUserForm) {
|
||||
this.setAuth(form);
|
||||
this.subject.next(this.wsSendWrapper(UserOperation.BanUser, form));
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.BanUser, form));
|
||||
}
|
||||
|
||||
public addAdmin(form: AddAdminForm) {
|
||||
this.setAuth(form);
|
||||
this.subject.next(this.wsSendWrapper(UserOperation.AddAdmin, form));
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.AddAdmin, form));
|
||||
}
|
||||
|
||||
public getUserDetails(form: GetUserDetailsForm) {
|
||||
this.setAuth(form, false);
|
||||
this.subject.next(this.wsSendWrapper(UserOperation.GetUserDetails, form));
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.GetUserDetails, form));
|
||||
}
|
||||
|
||||
public getReplies(form: GetRepliesForm) {
|
||||
this.setAuth(form);
|
||||
this.subject.next(this.wsSendWrapper(UserOperation.GetReplies, form));
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.GetReplies, form));
|
||||
}
|
||||
|
||||
public getUserMentions(form: GetUserMentionsForm) {
|
||||
this.setAuth(form);
|
||||
this.subject.next(this.wsSendWrapper(UserOperation.GetUserMentions, form));
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.GetUserMentions, form));
|
||||
}
|
||||
|
||||
public editUserMention(form: EditUserMentionForm) {
|
||||
this.setAuth(form);
|
||||
this.subject.next(this.wsSendWrapper(UserOperation.EditUserMention, form));
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.EditUserMention, form));
|
||||
}
|
||||
|
||||
public getModlog(form: GetModlogForm) {
|
||||
this.subject.next(this.wsSendWrapper(UserOperation.GetModlog, form));
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.GetModlog, form));
|
||||
}
|
||||
|
||||
public createSite(siteForm: SiteForm) {
|
||||
this.setAuth(siteForm);
|
||||
this.subject.next(this.wsSendWrapper(UserOperation.CreateSite, siteForm));
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.CreateSite, siteForm));
|
||||
}
|
||||
|
||||
public editSite(siteForm: SiteForm) {
|
||||
this.setAuth(siteForm);
|
||||
this.subject.next(this.wsSendWrapper(UserOperation.EditSite, siteForm));
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.EditSite, siteForm));
|
||||
}
|
||||
|
||||
public getSite() {
|
||||
this.subject.next(this.wsSendWrapper(UserOperation.GetSite, undefined));
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.GetSite, {}));
|
||||
}
|
||||
|
||||
public search(form: SearchForm) {
|
||||
this.setAuth(form, false);
|
||||
this.subject.next(this.wsSendWrapper(UserOperation.Search, form));
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.Search, form));
|
||||
}
|
||||
|
||||
public markAllAsRead() {
|
||||
let form = {};
|
||||
this.setAuth(form);
|
||||
this.subject.next(this.wsSendWrapper(UserOperation.MarkAllAsRead, form));
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.MarkAllAsRead, form));
|
||||
}
|
||||
|
||||
public saveUserSettings(userSettingsForm: UserSettingsForm) {
|
||||
this.setAuth(userSettingsForm);
|
||||
this.subject.next(
|
||||
this.ws.send(
|
||||
this.wsSendWrapper(UserOperation.SaveUserSettings, userSettingsForm)
|
||||
);
|
||||
}
|
||||
|
||||
public deleteAccount(form: DeleteAccountForm) {
|
||||
this.setAuth(form);
|
||||
this.subject.next(this.wsSendWrapper(UserOperation.DeleteAccount, form));
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.DeleteAccount, form));
|
||||
}
|
||||
|
||||
public passwordReset(form: PasswordResetForm) {
|
||||
this.subject.next(this.wsSendWrapper(UserOperation.PasswordReset, form));
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.PasswordReset, form));
|
||||
}
|
||||
|
||||
public passwordChange(form: PasswordChangeForm) {
|
||||
this.subject.next(this.wsSendWrapper(UserOperation.PasswordChange, form));
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.PasswordChange, form));
|
||||
}
|
||||
|
||||
public createPrivateMessage(form: PrivateMessageForm) {
|
||||
this.setAuth(form);
|
||||
this.subject.next(
|
||||
this.wsSendWrapper(UserOperation.CreatePrivateMessage, form)
|
||||
);
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.CreatePrivateMessage, form));
|
||||
}
|
||||
|
||||
public editPrivateMessage(form: EditPrivateMessageForm) {
|
||||
this.setAuth(form);
|
||||
this.subject.next(
|
||||
this.wsSendWrapper(UserOperation.EditPrivateMessage, form)
|
||||
);
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.EditPrivateMessage, form));
|
||||
}
|
||||
|
||||
public getPrivateMessages(form: GetPrivateMessagesForm) {
|
||||
this.setAuth(form);
|
||||
this.subject.next(
|
||||
this.wsSendWrapper(UserOperation.GetPrivateMessages, form)
|
||||
);
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.GetPrivateMessages, form));
|
||||
}
|
||||
|
||||
private wsSendWrapper(op: UserOperation, data: MessageType) {
|
||||
let send = { op: UserOperation[op], data: data };
|
||||
console.log(send);
|
||||
return send;
|
||||
return JSON.stringify(send);
|
||||
}
|
||||
|
||||
private setAuth(obj: any, throwErr: boolean = true) {
|
||||
|
@ -325,6 +312,5 @@ export class WebSocketService {
|
|||
}
|
||||
|
||||
window.onbeforeunload = () => {
|
||||
WebSocketService.Instance.subject.unsubscribe();
|
||||
WebSocketService.Instance.subject = null;
|
||||
WebSocketService.Instance.ws.close();
|
||||
};
|
||||
|
|
70
ui/src/translations/de.ts
vendored
70
ui/src/translations/de.ts
vendored
|
@ -15,9 +15,9 @@ export const de = {
|
|||
remove_comment: 'Kommentar löschen',
|
||||
communities: 'Communities',
|
||||
users: 'Benutzer',
|
||||
create_a_community: 'Eine community anlegen',
|
||||
create_community: 'Community anlegen',
|
||||
remove_community: 'Community entfernen',
|
||||
create_a_community: 'Eine Gemeinschaft anlegen',
|
||||
create_community: 'Gemeinschaft anlegen',
|
||||
remove_community: 'Gemeinschaft entfernen',
|
||||
subscribed_to_communities: 'Abonnierte <1>communities</1>',
|
||||
trending_communities: 'Trending <1>communities</1>',
|
||||
list_of_communities: 'Liste von communities',
|
||||
|
@ -36,17 +36,17 @@ export const de = {
|
|||
unsticky: 'nicht haftend',
|
||||
link: 'link',
|
||||
archive_link: 'Archiv-Link',
|
||||
mod: 'mod',
|
||||
mods: 'mods',
|
||||
mod: 'Moderator',
|
||||
mods: 'Moderatoren',
|
||||
moderates: 'Moderiert',
|
||||
settings: 'Einstellungen',
|
||||
remove_as_mod: 'Als mod entfernen',
|
||||
appoint_as_mod: 'Zum mod ernennen',
|
||||
remove_as_mod: 'Als Moderator entfernen',
|
||||
appoint_as_mod: 'Zum Moderator ernennen',
|
||||
modlog: 'Modlog',
|
||||
admin: 'admin',
|
||||
admins: 'admins',
|
||||
remove_as_admin: 'Als admin entfernen',
|
||||
appoint_as_admin: 'Zum admin ernennen',
|
||||
admin: 'Administrator',
|
||||
admins: 'Administratoren',
|
||||
remove_as_admin: 'Als Administrator entfernen',
|
||||
appoint_as_admin: 'Zum Administrator ernennen',
|
||||
remove: 'entfernen',
|
||||
removed: 'entfernt',
|
||||
locked: 'gesperrt',
|
||||
|
@ -66,11 +66,11 @@ export const de = {
|
|||
unban_from_site: 'Von der Seite entbannen',
|
||||
banned: 'gesperrt',
|
||||
save: 'speichern',
|
||||
unsave: 'unsave',
|
||||
unsave: 'nicht speichern',
|
||||
create: 'anlegen',
|
||||
creator: 'Ersteller',
|
||||
username: 'Username',
|
||||
email_or_username: 'Email oder Username',
|
||||
username: 'Benutzername',
|
||||
email_or_username: 'E-mail oder Username',
|
||||
number_of_users: '{{count}} Benutzer',
|
||||
number_of_subscribers: '{{count}} Abonnenten',
|
||||
number_of_points: '{{count}} Punkte',
|
||||
|
@ -86,7 +86,7 @@ export const de = {
|
|||
subscribed: 'Abonniert',
|
||||
prev: 'Zurück',
|
||||
next: 'Weiter',
|
||||
sidebar: 'Sidebar',
|
||||
sidebar: 'Seitenleiste',
|
||||
sort_type: 'Sortieren nach',
|
||||
hot: 'Hot',
|
||||
new: 'Neu',
|
||||
|
@ -116,28 +116,29 @@ export const de = {
|
|||
password: 'Passwort',
|
||||
verify_password: 'Passwort überprüfen',
|
||||
forgot_password: 'Passwort vergessen',
|
||||
reset_password_mail_sent: 'Eine E-Mail wurde geschickt, um dein Passwort zurückzusetzen.',
|
||||
reset_password_mail_sent:
|
||||
'Eine E-Mail wurde geschickt, um dein Passwort zurückzusetzen.',
|
||||
password_change: 'Passwort geändert',
|
||||
new_password: 'neues Passwort',
|
||||
no_email_setup: "Dieser Server hat E-Mails nicht korrekt eingerichtet.",
|
||||
no_email_setup: 'Dieser Server hat E-Mails nicht korrekt eingerichtet.',
|
||||
login: 'Einloggen',
|
||||
sign_up: 'Registrieren',
|
||||
email: 'Email',
|
||||
optional: 'Optional',
|
||||
email: 'E-Mail',
|
||||
optional: 'optional',
|
||||
expires: 'Ablaufdatum',
|
||||
language: 'Sprache',
|
||||
browser_default: 'Standard-Browser',
|
||||
url: 'URL',
|
||||
body: 'Text',
|
||||
copy_suggested_title: 'Vorgeschlagenen Titel übernehmen: {{title}}',
|
||||
community: 'Community',
|
||||
expand_here: 'Expand here',
|
||||
community: 'Gemeinschaft',
|
||||
expand_here: 'hier erweitern',
|
||||
subscribe_to_communities: 'Abonniere ein paar <1>communities</1>.',
|
||||
chat: 'Chat',
|
||||
recent_comments: 'Neueste Kommentare',
|
||||
no_results: 'Keine Ergebnisse.',
|
||||
setup: 'Setup',
|
||||
lemmy_instance_setup: 'Lemmy Instanz Setup',
|
||||
setup: 'Einrichten',
|
||||
lemmy_instance_setup: 'Lemmy Instanz Einrichten',
|
||||
setup_admin: 'Seiten Administrator konfigurieren',
|
||||
your_site: 'deine Seite',
|
||||
modified: 'verändert',
|
||||
|
@ -151,7 +152,7 @@ export const de = {
|
|||
support_on_patreon: 'Auf Patreon unterstützen',
|
||||
general_sponsors:
|
||||
'Allgemeine Sponsoren sind die, die zwischen $10 und $39 zu Lemmy beitragen.',
|
||||
crypto: 'Crypto',
|
||||
crypto: 'Kryptowährung',
|
||||
bitcoin: 'Bitcoin',
|
||||
ethereum: 'Ethereum',
|
||||
monero: 'Monero',
|
||||
|
@ -159,16 +160,16 @@ export const de = {
|
|||
joined: 'beigetreten',
|
||||
by: 'von',
|
||||
to: 'bis',
|
||||
transfer_community: 'Transfer-Community',
|
||||
transfer_community: 'Gemeinschaft übertragen',
|
||||
transfer_site: 'Transferseite',
|
||||
are_you_sure: 'Bist du sicher?',
|
||||
yes: 'Ja',
|
||||
no: 'Nein',
|
||||
powered_by: 'Bereitgestellt durch',
|
||||
landing_0:
|
||||
'Lemmy ist ein <1>Link Aggregator</1> / Reddit Alternative im <2>Fediverse</2>.<3></3>Es ist selbst-hostbar, hat live-updates von Kommentar-threads und ist winzig (<4>~80kB</4>). Federation in das ActivityPub Netzwerk ist geplant. <5></5>Dies ist eine <6>sehr frühe Beta Version</6>, und viele Features funktionieren zurzeit nicht richtig oder fehlen. <7></7>Schlage neue Features vor oder melde Bugs <8>hier.</8><9></9>Gebaut mit <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.',
|
||||
'Lemmy ist ein <1>Link-Aggregator</1> / Reddit Alternative im <2>Fediverse</2>.<3></3>Es ist selbst-hostbar, hat live-updates von Kommentar-threads und ist winzig (<4>~80kB</4>). Federation in das ActivityPub Netzwerk ist geplant. <5></5>Dies ist eine <6>sehr frühe Beta Version</6>, und viele Features funktionieren zurzeit nicht richtig oder fehlen. <7></7>Schlage neue Features vor oder melde Bugs <8>hier.</8><9></9>Gebaut mit <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.',
|
||||
not_logged_in: 'Nicht eingeloggt.',
|
||||
community_ban: 'Du wurdest von dieser Community gebannt.',
|
||||
community_ban: 'Du wurdest von dieser Gemeinschaft gebannt.',
|
||||
site_ban: 'Du wurdest von dieser Seite gebannt',
|
||||
couldnt_create_comment: 'Konnte Kommentar nicht anlegen.',
|
||||
couldnt_like_comment: 'Konnte nicht liken.',
|
||||
|
@ -176,14 +177,15 @@ export const de = {
|
|||
couldnt_save_comment: 'Konnte Kommentar nicht speichern.',
|
||||
no_comment_edit_allowed: 'Keine Erlaubnis Kommentar zu editieren.',
|
||||
no_post_edit_allowed: 'Keine Erlaubnis Beitrag zu editieren.',
|
||||
no_community_edit_allowed: 'Keine Erlaubnis Community zu editieren.',
|
||||
couldnt_find_community: 'Konnte Community nicht finden.',
|
||||
couldnt_update_community: 'Konnte Community nicht aktualisieren.',
|
||||
community_already_exists: 'Community existiert bereits.',
|
||||
no_community_edit_allowed: 'Keine Erlaubnis Gemeinschaft zu editieren.',
|
||||
couldnt_find_community: 'Konnte Gemeinschaft nicht finden.',
|
||||
couldnt_update_community: 'Konnte Gemeinschaft nicht aktualisieren.',
|
||||
community_already_exists: 'Gemeinschaft existiert bereits.',
|
||||
community_moderator_already_exists:
|
||||
'Community Moderator existiert bereits.',
|
||||
community_follower_already_exists: 'Community Follower existiert bereits.',
|
||||
community_user_already_banned: 'Community Nutzer schon gebannt.',
|
||||
'Gemeinschaft Moderator existiert bereits.',
|
||||
community_follower_already_exists:
|
||||
'Gemeinschaft Follower existiert bereits.',
|
||||
community_user_already_banned: 'Gemeinschaft Nutzer schon gebannt.',
|
||||
couldnt_create_post: 'Konnte Beitrag nicht anlegen.',
|
||||
couldnt_like_post: 'Konnte Beitrag nicht liken.',
|
||||
couldnt_find_post: 'Konnte Beitrag nicht finden.',
|
||||
|
|
2
ui/src/translations/en.ts
vendored
2
ui/src/translations/en.ts
vendored
|
@ -230,5 +230,7 @@ export const en = {
|
|||
couldnt_create_private_message: "Couldn't create private message.",
|
||||
no_private_message_edit_allowed: 'Not allowed to edit private message.',
|
||||
couldnt_update_private_message: "Couldn't update private message.",
|
||||
time: 'Time',
|
||||
action: 'Action',
|
||||
},
|
||||
};
|
||||
|
|
21
ui/src/translations/es.ts
vendored
21
ui/src/translations/es.ts
vendored
|
@ -132,7 +132,7 @@ export const es = {
|
|||
reset_password_mail_sent: 'Enviar correo para reestablecer la contraseña.',
|
||||
password_change: 'Cambio de Contraseña',
|
||||
new_password: 'Nueva Contraseña',
|
||||
no_email_setup: "Este servidor no ha activado correctamente el correo.",
|
||||
no_email_setup: 'Este servidor no ha activado correctamente el correo.',
|
||||
email: 'Correo electrónico',
|
||||
matrix_user_id: 'Usuario Matricial',
|
||||
private_message_disclaimer:
|
||||
|
@ -204,9 +204,12 @@ export const es = {
|
|||
couldnt_find_community: 'No se pudo encontrar la comunidad.',
|
||||
couldnt_update_community: 'No se pudo actualizar la comunidad.',
|
||||
community_already_exists: 'Esta comunidad ya existe.',
|
||||
community_moderator_already_exists: 'Este moderador de la comunidad ya existe.',
|
||||
community_follower_already_exists: 'Este seguidor de la comunidad ya existe.',
|
||||
community_user_already_banned: 'Este usuario de la comunidad ya fue expulsado.',
|
||||
community_moderator_already_exists:
|
||||
'Este moderador de la comunidad ya existe.',
|
||||
community_follower_already_exists:
|
||||
'Este seguidor de la comunidad ya existe.',
|
||||
community_user_already_banned:
|
||||
'Este usuario de la comunidad ya fue expulsado.',
|
||||
couldnt_create_post: 'No se pudo crear la publicación.',
|
||||
couldnt_like_post: 'No se pudo gustar la publicación.',
|
||||
couldnt_find_post: 'No se pudo encontrar la publicación.',
|
||||
|
@ -225,9 +228,11 @@ export const es = {
|
|||
user_already_exists: 'El usuario ya existe.',
|
||||
email_already_exists: 'El correo ya está en uso.',
|
||||
couldnt_update_user: 'No se pudo actualizar el usuario.',
|
||||
system_err_login: 'Error del sistema. Intente cerrar sesión e ingresar de nuevo.',
|
||||
couldnt_create_private_message: "No se pudo crear el mensaje privado.",
|
||||
no_private_message_edit_allowed: 'Sin permisos para editar el mensaje privado.',
|
||||
couldnt_update_private_message: "No se pudo actualizar el mensaje privado.",
|
||||
system_err_login:
|
||||
'Error del sistema. Intente cerrar sesión e ingresar de nuevo.',
|
||||
couldnt_create_private_message: 'No se pudo crear el mensaje privado.',
|
||||
no_private_message_edit_allowed:
|
||||
'Sin permisos para editar el mensaje privado.',
|
||||
couldnt_update_private_message: 'No se pudo actualizar el mensaje privado.',
|
||||
},
|
||||
};
|
||||
|
|
169
ui/src/translations/fa.ts
vendored
Normal file
169
ui/src/translations/fa.ts
vendored
Normal file
|
@ -0,0 +1,169 @@
|
|||
export const fa = {
|
||||
translation: {
|
||||
post: 'مطلب',
|
||||
remove_post: 'حذف مطلب',
|
||||
no_posts: 'بدون مطلب.',
|
||||
create_a_post: 'ایجاد یک مطلب',
|
||||
create_post: 'ایجاد مطلب',
|
||||
number_of_posts: '{{count}} مطلب',
|
||||
posts: 'مطالب',
|
||||
related_posts: 'این مطالب ممکن است مرتبط باشند',
|
||||
cross_posts: 'این پیوند در اینجا هم منتشر شده:',
|
||||
comments: 'نظرات',
|
||||
number_of_comments: '{{count}} نظر',
|
||||
remove_comment: 'حذف نظر',
|
||||
communities: 'جوامع',
|
||||
users: 'کاربران',
|
||||
create_a_community: 'ایجاد یک جامعه جدید',
|
||||
create_community: 'ایجاد جامعه',
|
||||
remove_community: 'حذف جامعه',
|
||||
list_of_communities: 'فهرست جوامع',
|
||||
number_of_communities: '{{count}} جامعه',
|
||||
community_reqs: 'حروف کوچک, زیرخط, و بدون فاصله.',
|
||||
edit: 'ویرایش',
|
||||
reply: 'پاسخ',
|
||||
cancel: 'لغو',
|
||||
preview: 'پیشنمایش',
|
||||
upload_image: 'بارگذاری تصویر',
|
||||
avatar: 'آواتار',
|
||||
upload_avatar: 'بارگذاری آواتار',
|
||||
show_avatars: 'نمایش آواتارها',
|
||||
formatting_help: 'راهنمای قالببندی',
|
||||
view_source: 'نمایش منبع',
|
||||
unlock: 'بازکردن قفل',
|
||||
lock: 'قفل کردن',
|
||||
sticky: 'چسبان',
|
||||
unsticky: 'غیرچسبان',
|
||||
link: 'پیوند',
|
||||
archive_link: 'بایگاهی پیوند',
|
||||
settings: 'تنظیمات',
|
||||
admin: 'مدیر',
|
||||
admins: 'مدیران',
|
||||
remove_as_admin: 'حذف به عنوان مدیر',
|
||||
appoint_as_admin: 'انتصاب به عنوان مدیر',
|
||||
remove: 'حذف',
|
||||
removed: 'حذف شد',
|
||||
locked: 'قفل شد',
|
||||
reason: 'دلیل',
|
||||
mark_as_read: 'علامتگذاری به عنوان خوانده شده',
|
||||
mark_as_unread: 'علامتگذاری به عنوان خوانده نشده',
|
||||
delete: 'پاک کردن',
|
||||
deleted: 'پاک شد',
|
||||
delete_account: 'پاک کردن حساب',
|
||||
delete_account_confirm:
|
||||
'هشدار: این کنش، تمام اطلاعات شما را برای همیشه پاک میکند. برای تایید، گذرواژه خود را وارد کنید.',
|
||||
restore: 'بازگردانی',
|
||||
save: 'ذخیره',
|
||||
unsave: 'عدم ذخیره',
|
||||
create: 'ایجاد',
|
||||
creator: 'سازنده',
|
||||
username: 'نامکاربری',
|
||||
email_or_username: 'رایانامه یا نامکاربری',
|
||||
number_of_users: '{{count}} کاربر',
|
||||
number_of_points: '{{count}} امتیاز',
|
||||
number_online: '{{count}} کاربر برخط',
|
||||
name: 'نام',
|
||||
title: 'عنوان',
|
||||
category: 'دستهبندی',
|
||||
prev: 'پیش',
|
||||
next: 'بعد',
|
||||
sidebar: 'نوار کناری',
|
||||
sort_type: 'نوع ترتیب',
|
||||
hot: 'داغ',
|
||||
new: 'تازه',
|
||||
top_day: 'بهترینهای روز',
|
||||
week: 'هفته',
|
||||
month: 'ماه',
|
||||
year: 'سال',
|
||||
all: 'همه',
|
||||
top: 'بالاترین',
|
||||
mark_all_as_read: 'علامت زدن همه به عنوان خوانده شده',
|
||||
type: 'نوع',
|
||||
unread: 'خواندهنشده',
|
||||
replies: 'پاسخها',
|
||||
mentions: 'اشارهها',
|
||||
reply_sent: 'پاسخ فرستاده شد',
|
||||
search: 'جستجو',
|
||||
overview: 'دید کلی',
|
||||
view: 'نما',
|
||||
logout: 'خروج',
|
||||
login_sign_up: 'ورود / نامنویسی',
|
||||
login: 'ورود',
|
||||
sign_up: 'نامنویسی',
|
||||
unread_messages: 'پیامهای خوانده نشده',
|
||||
password: 'گذرواژه',
|
||||
verify_password: 'تایید گذرواژه',
|
||||
old_password: 'پسورد پیشین',
|
||||
forgot_password: 'گذرواژه را فراموش کردهام',
|
||||
reset_password_mail_sent: 'رایانامهای برای بازنشانی گذرواژه فرستاده شد.',
|
||||
password_change: 'تغییر گذرواژه',
|
||||
new_password: 'گذرواژه جدید',
|
||||
email: 'رایانامه',
|
||||
send_notifications_to_email: 'فرستادن اعلانات به رایانامه',
|
||||
optional: 'انتخابی',
|
||||
expires: 'منقضی شود',
|
||||
language: 'زبان',
|
||||
browser_default: 'پیشفرض مرورگر',
|
||||
downvotes_disabled: 'رای پایین غیرفعال است',
|
||||
enable_downvotes: 'فعالسازی رای پایین',
|
||||
open_registration: 'باز کردن نامنویسی',
|
||||
registration_closed: 'نامنویسی بسته است',
|
||||
enable_nsfw: 'فعالسازی NSFW',
|
||||
chat: 'گپ',
|
||||
recent_comments: 'نظرات اخیر',
|
||||
no_results: 'بدون نتیجه.',
|
||||
setup: 'نصب',
|
||||
lemmy_instance_setup: 'نصب نمونهٔ لمی',
|
||||
setup_admin: 'نصب مدیریت پایگاه',
|
||||
your_site: 'پایگاه شما',
|
||||
modified: 'تغییر یافت',
|
||||
nsfw: 'NSFW',
|
||||
show_nsfw: 'نمایش محتوای NSFW',
|
||||
sponsors: 'حامیان',
|
||||
sponsors_of_lemmy: 'حامیان لمی',
|
||||
support_on_patreon: 'حمایت روی Patreon',
|
||||
donate_to_lemmy: 'اعطای اعانه به لمی',
|
||||
donate: 'اعانه',
|
||||
crypto: 'رمزارز',
|
||||
bitcoin: 'بیتکوین',
|
||||
ethereum: 'اتریوم',
|
||||
monero: 'مونرو',
|
||||
code: 'کد',
|
||||
transfer_community: 'انتقال جامعه',
|
||||
transfer_site: 'انتقال پایگاه',
|
||||
are_you_sure: 'مطمئنید؟',
|
||||
yes: 'بله',
|
||||
no: 'خیر',
|
||||
powered_by: 'نیرو گرفته از',
|
||||
not_logged_in: 'وارد نشدهاید.',
|
||||
community_ban: 'فعالیت شما در این جامعه ممنوع شده است.',
|
||||
site_ban: 'فعالیت شما در این پایگاه ممنوع شده است',
|
||||
couldnt_create_comment: 'ناتوانی در ایجاد نظر.',
|
||||
couldnt_like_comment: 'ناتوانی در پسنیدن نظر.',
|
||||
couldnt_update_comment: 'ناتوانی در بهروزرسانی نظر.',
|
||||
couldnt_save_comment: 'ناتوانی در ذخیره نظر.',
|
||||
no_comment_edit_allowed: 'مجاز به ویرایش نظر نیستید.',
|
||||
no_post_edit_allowed: 'مجاز به ویرایش مطلب نیستید.',
|
||||
no_community_edit_allowed: 'مجاز به ویرایش جامعه نیستید.',
|
||||
couldnt_find_community: 'ناتوانی در یافتن جامعه.',
|
||||
couldnt_update_community: 'ناتوانی در بهروزرسانی جامعه.',
|
||||
community_already_exists: 'این جامعه از قبل وجود داشته است.',
|
||||
couldnt_create_post: 'ناتوانی در ایجاد مطلب.',
|
||||
couldnt_like_post: 'ناتوانی در پسندیدن مطلب.',
|
||||
couldnt_find_post: 'ناتوانی در یافتن مطلب.',
|
||||
couldnt_get_posts: 'ناتوانی در دریافت مطالب',
|
||||
couldnt_update_post: 'ناتوای در بهروزرسانی مطلب',
|
||||
couldnt_save_post: 'ناتوانی در ذخیره مطلب.',
|
||||
not_an_admin: 'مدیر نیستید.',
|
||||
site_already_exists: 'این پایگاه از قبل وجود داشته است.',
|
||||
couldnt_update_site: 'ناتوانی در بهروزرسانی پایگاه.',
|
||||
couldnt_find_that_username_or_email:
|
||||
'ناتوانی در یافتن این نام کاربری یا رایانامه.',
|
||||
password_incorrect: 'گذرواژه نادرست.',
|
||||
passwords_dont_match: 'گذرواژهها با هم منطبق نیستند.',
|
||||
user_already_exists: 'این کاربر از قبل وجود دارد.',
|
||||
email_already_exists: 'این رایانامه از قبل وجود دارد.',
|
||||
couldnt_update_user: 'ناتوانی در بهروزرسانی کاربر.',
|
||||
system_err_login: 'خطای سامانه. سعی کنید خارج شده و دوباره وارد شوید.',
|
||||
},
|
||||
};
|
49
ui/src/translations/fi.ts
vendored
49
ui/src/translations/fi.ts
vendored
|
@ -22,7 +22,8 @@ export const fi = {
|
|||
trending_communities: 'Nousevat <1>yhteisöt</1>',
|
||||
list_of_communities: 'Lista yhteisöistä',
|
||||
number_of_communities: '{{count}} yhteisöä',
|
||||
community_reqs: 'pienillä kirjaimilla, alleviivauksella, eikä välilyöntejä.',
|
||||
community_reqs:
|
||||
'pienillä kirjaimilla, alleviivauksella, eikä välilyöntejä.',
|
||||
create_private_message: 'Luo yksityisviesti',
|
||||
send_secure_message: 'Lähetä suojattu viesti',
|
||||
send_message: 'Lähetä viesti',
|
||||
|
@ -132,7 +133,7 @@ export const fi = {
|
|||
reset_password_mail_sent: 'Sähköposti lähetettiin salasanan nollaamiseksi.',
|
||||
password_change: 'Salasanan muutos',
|
||||
new_password: 'Uusi salasana',
|
||||
no_email_setup: "Tämä palvelin ei ole asettanut sähköpostia oikein.",
|
||||
no_email_setup: 'Tämä palvelin ei ole asettanut sähköpostia oikein.',
|
||||
email: 'Sähköposti',
|
||||
matrix_user_id: ' Matrix-käyttäjä',
|
||||
private_message_disclaimer:
|
||||
|
@ -189,45 +190,47 @@ export const fi = {
|
|||
no: 'ei',
|
||||
powered_by: 'Vauhdittajana',
|
||||
landing_0:
|
||||
"Lemmy on <1>linkinkerääjä</1> / Reddit-vaihtoehto, tarkoitettu toimimaan <2>fediversessä</2>.<3></3>Sitä voi isännöidä itse, siinä on tosiaikaisesti päivittyvät kommenttiketjut, ja se on pieni (<4>~80 kilotavua</4>). Federointi ActivityPub-verkkoon on suunnittelun alla. <5></5>Tämä on <6>hyvin varhainen betaversio</6>, ja monet ominaisuudet ovat toistaiseksi rikki tai poissa. <7></7>Ehdota uusia ominaisuuksia tai raportoi bugeja <8>tänne.</8><9></9>Tehty teknologioilla <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.",
|
||||
'Lemmy on <1>linkinkerääjä</1> / Reddit-vaihtoehto, tarkoitettu toimimaan <2>fediversessä</2>.<3></3>Sitä voi isännöidä itse, siinä on tosiaikaisesti päivittyvät kommenttiketjut, ja se on pieni (<4>~80 kilotavua</4>). Federointi ActivityPub-verkkoon on suunnittelun alla. <5></5>Tämä on <6>hyvin varhainen betaversio</6>, ja monet ominaisuudet ovat toistaiseksi rikki tai poissa. <7></7>Ehdota uusia ominaisuuksia tai raportoi bugeja <8>tänne.</8><9></9>Tehty teknologioilla <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.',
|
||||
not_logged_in: 'Ei kirjautunut sisään.',
|
||||
logged_in: 'Kirjautunut sisään.',
|
||||
community_ban: 'Sinulle on asetettu porttikielto tähän yhteisöön.',
|
||||
site_ban: 'Sinut on asetettu porttikieltoon tältä sivustolta',
|
||||
couldnt_create_comment: "Kommenttia ei pystytty luomaan.",
|
||||
couldnt_like_comment: "Kommentista ei voitu tykätä.",
|
||||
couldnt_update_comment: "Kommenttia ei voitu päivittää.",
|
||||
couldnt_save_comment: "Kommenttia ei voitu tallentaa.",
|
||||
couldnt_create_comment: 'Kommenttia ei pystytty luomaan.',
|
||||
couldnt_like_comment: 'Kommentista ei voitu tykätä.',
|
||||
couldnt_update_comment: 'Kommenttia ei voitu päivittää.',
|
||||
couldnt_save_comment: 'Kommenttia ei voitu tallentaa.',
|
||||
no_comment_edit_allowed: 'Et ole sallittu muokkaamaan kommenttia.',
|
||||
no_post_edit_allowed: 'Et ole sallittu muokkaamaan viestiä.',
|
||||
no_community_edit_allowed: 'Et ole sallittu muokkaamaan yhteisöä.',
|
||||
couldnt_find_community: "Yhteisöä ei voitu löytää.",
|
||||
couldnt_update_community: "Yhteisöä ei voitu päivittää.",
|
||||
couldnt_find_community: 'Yhteisöä ei voitu löytää.',
|
||||
couldnt_update_community: 'Yhteisöä ei voitu päivittää.',
|
||||
community_already_exists: 'Yhteisö on jo olemassa.',
|
||||
community_moderator_already_exists: 'Yhteisön moderaattori on jo olemassa.',
|
||||
community_follower_already_exists: 'Yhteisön seuraaja on jo olemassa.',
|
||||
community_user_already_banned: 'Yhteisön käyttäjä on jo porttikiellossa.',
|
||||
couldnt_create_post: "Ei voitu luoda viestiä.",
|
||||
couldnt_like_post: "Viestistä ei voitu tykätä.",
|
||||
couldnt_find_post: "Viestiä ei löytynyt.",
|
||||
couldnt_get_posts: "Viestejä ei saatu",
|
||||
couldnt_update_post: "Viestiä ei voitu päivittää",
|
||||
couldnt_save_post: "Viestiä ei voitu tallentaa.",
|
||||
couldnt_create_post: 'Ei voitu luoda viestiä.',
|
||||
couldnt_like_post: 'Viestistä ei voitu tykätä.',
|
||||
couldnt_find_post: 'Viestiä ei löytynyt.',
|
||||
couldnt_get_posts: 'Viestejä ei saatu',
|
||||
couldnt_update_post: 'Viestiä ei voitu päivittää',
|
||||
couldnt_save_post: 'Viestiä ei voitu tallentaa.',
|
||||
no_slurs: 'Ei loukkauksia.',
|
||||
not_an_admin: 'Ei ole ylläpitäjä.',
|
||||
site_already_exists: 'Sivusto on jo olemassa.',
|
||||
couldnt_update_site: "Sivustoa ei voitu päivittää.",
|
||||
couldnt_update_site: 'Sivustoa ei voitu päivittää.',
|
||||
couldnt_find_that_username_or_email:
|
||||
"Käyttäjänimeä tai sähköpostia ei onnistuttu löytämään.",
|
||||
'Käyttäjänimeä tai sähköpostia ei onnistuttu löytämään.',
|
||||
password_incorrect: 'Salasana on väärin.',
|
||||
passwords_dont_match: 'Salasanat eivät täsmää.',
|
||||
admin_already_created: "Anteeksi, mutta täällä on jo ylläpitäjä.",
|
||||
admin_already_created: 'Anteeksi, mutta täällä on jo ylläpitäjä.',
|
||||
user_already_exists: 'Käyttäjä on jo olemassa.',
|
||||
email_already_exists: 'Sähköposti on jo olemassa.',
|
||||
couldnt_update_user: "Käyttäjää ei voitu päivittää.",
|
||||
system_err_login: 'Järjestelmävirhe. Yritä kirjautua ulos ja kirjautua uudestaan sisään.',
|
||||
couldnt_create_private_message: "Yksityisviestiä ei voitu luoda.",
|
||||
no_private_message_edit_allowed: 'Et ole sallittu muokkaamaan yksityisviestiä.',
|
||||
couldnt_update_private_message: "Yksityisviestiä ei voitu päivittää.",
|
||||
couldnt_update_user: 'Käyttäjää ei voitu päivittää.',
|
||||
system_err_login:
|
||||
'Järjestelmävirhe. Yritä kirjautua ulos ja kirjautua uudestaan sisään.',
|
||||
couldnt_create_private_message: 'Yksityisviestiä ei voitu luoda.',
|
||||
no_private_message_edit_allowed:
|
||||
'Et ole sallittu muokkaamaan yksityisviestiä.',
|
||||
couldnt_update_private_message: 'Yksityisviestiä ei voitu päivittää.',
|
||||
},
|
||||
};
|
||||
|
|
19
ui/src/translations/nl.ts
vendored
19
ui/src/translations/nl.ts
vendored
|
@ -211,6 +211,23 @@ export const nl = {
|
|||
open_registration: 'Open registratie',
|
||||
registration_closed: 'Registratie gesloten',
|
||||
enable_nsfw: 'NSFW toestaan',
|
||||
theme: 'Thema'
|
||||
theme: 'Thema',
|
||||
create_private_message: 'Maak een beveiligd bericht',
|
||||
send_secure_message: 'Verstuur beveiligd bericht',
|
||||
send_message: 'Verstuur bericht',
|
||||
message: 'Bericht',
|
||||
old: 'Oud',
|
||||
message_sent: 'Bericht verstuurd',
|
||||
messages: 'Berichten',
|
||||
matrix_user_id: 'Matrix gebruikers-id',
|
||||
private_message_disclaimer: 'Waarschuwing: Privé berichten in Lemmy zijn niet beveiligd. Maak een account aan op <1>Riot.im</1> om veilig te communiceren',
|
||||
donate_to_lemmy: 'Doneer aan Lemmy',
|
||||
donate: 'Doneer',
|
||||
from: 'van',
|
||||
logged_in: 'Ingelogd',
|
||||
email_already_exists: 'Email bestaat al',
|
||||
couldnt_create_private_message: 'Kan beveiligd bericht niet maken',
|
||||
no_private_message_edit_allowed: 'Niet toegestaan om privé berichten te wijzigen',
|
||||
couldnt_update_private_message: 'Kan beveiligd bericht niet bijwerken'
|
||||
},
|
||||
};
|
||||
|
|
30
ui/src/translations/zh.ts
vendored
30
ui/src/translations/zh.ts
vendored
|
@ -25,14 +25,14 @@ export const zh = {
|
|||
unlock: '解锁',
|
||||
lock: '加锁',
|
||||
link: '链接',
|
||||
mod: 'mod',
|
||||
mods: 'mods',
|
||||
moderates: 'Moderates',
|
||||
remove_as_mod: 'remove as mod',
|
||||
appoint_as_mod: 'appoint as mod',
|
||||
modlog: 'Modlog',
|
||||
admin: 'admin',
|
||||
admins: 'admins',
|
||||
mod: '监管人',
|
||||
mods: '监管人',
|
||||
moderates: '监管',
|
||||
remove_as_mod: '添加监管人',
|
||||
appoint_as_mod: '移除监管人',
|
||||
modlog: '监管记录',
|
||||
admin: '管理权限',
|
||||
admins: '管理权限',
|
||||
remove_as_admin: '移除管理权限',
|
||||
appoint_as_admin: '添加管理权限',
|
||||
remove: '移除',
|
||||
|
@ -77,7 +77,7 @@ export const zh = {
|
|||
year: '年',
|
||||
all: '所有',
|
||||
top: '最热',
|
||||
api: 'API',
|
||||
api: '应用程式介面',
|
||||
inbox: '收件箱',
|
||||
inbox_for: '<1>{{user}}</1> 收件箱',
|
||||
mark_all_as_read: '标记所有已读',
|
||||
|
@ -98,7 +98,7 @@ export const zh = {
|
|||
email: '邮箱',
|
||||
optional: '选项',
|
||||
expires: '过期',
|
||||
url: 'URL',
|
||||
url: '网址',
|
||||
body: '内容',
|
||||
copy_suggested_title: '复制建议的标题: {{title}}',
|
||||
community: '节点',
|
||||
|
@ -111,11 +111,11 @@ export const zh = {
|
|||
setup_admin: '设置管理员',
|
||||
your_site: '你的站点',
|
||||
modified: '修改',
|
||||
sponsors: 'Sponsors',
|
||||
sponsors_of_lemmy: 'Sponsors of Lemmy',
|
||||
sponsors: '发起人',
|
||||
sponsors_of_lemmy: 'Lemmy 的发起人',
|
||||
sponsor_message:
|
||||
'Lemmy is free, <1>open-source</1> software, meaning no advertising, monetizing, or venture capital, ever. Your donations directly support full-time development of the project. Thank you to the following people:',
|
||||
support_on_patreon: 'Support on Patreon',
|
||||
support_on_patreon: '在 Patreon 赞助',
|
||||
general_sponsors:
|
||||
'General Sponsors are those that pledged $10 to $39 to Lemmy.',
|
||||
crypto: '加密',
|
||||
|
@ -139,8 +139,8 @@ export const zh = {
|
|||
couldnt_find_community: '不能找到节点.',
|
||||
couldnt_update_community: '不能更新节点.',
|
||||
community_already_exists: '节点已存在.',
|
||||
community_moderator_already_exists: '节点 moderator 已存在.',
|
||||
community_follower_already_exists: '节点 follower 已存在.',
|
||||
community_moderator_already_exists: '节点监管人已存在.',
|
||||
community_follower_already_exists: '节点追随者已存在.',
|
||||
community_user_already_banned: '节点用户已禁止.',
|
||||
couldnt_create_post: '不能创建帖子.',
|
||||
couldnt_like_post: '不能收藏帖子.',
|
||||
|
|
6
ui/src/utils.ts
vendored
6
ui/src/utils.ts
vendored
|
@ -9,6 +9,7 @@ import 'moment/locale/nl';
|
|||
import 'moment/locale/it';
|
||||
import 'moment/locale/fi';
|
||||
import 'moment/locale/ca';
|
||||
import 'moment/locale/fa';
|
||||
|
||||
import {
|
||||
UserOperation,
|
||||
|
@ -258,6 +259,7 @@ export const languages = [
|
|||
{ code: 'eo', name: 'Esperanto' },
|
||||
{ code: 'es', name: 'Español' },
|
||||
{ code: 'de', name: 'Deutsch' },
|
||||
{ code: 'fa', name: 'فارسی' },
|
||||
{ code: 'zh', name: '中文' },
|
||||
{ code: 'fi', name: 'Suomi' },
|
||||
{ code: 'fr', name: 'Français' },
|
||||
|
@ -306,6 +308,8 @@ export function getMomentLanguage(): string {
|
|||
lang = 'fi';
|
||||
} else if (lang.startsWith('ca')) {
|
||||
lang = 'ca';
|
||||
} else if (lang.startsWith('fa')) {
|
||||
lang = 'fa';
|
||||
} else {
|
||||
lang = 'en';
|
||||
}
|
||||
|
@ -314,6 +318,7 @@ export function getMomentLanguage(): string {
|
|||
|
||||
export const themes = [
|
||||
'litera',
|
||||
'materia',
|
||||
'minty',
|
||||
'solar',
|
||||
'united',
|
||||
|
@ -323,6 +328,7 @@ export const themes = [
|
|||
'sketchy',
|
||||
'vaporwave',
|
||||
'vaporwave-dark',
|
||||
'i386',
|
||||
];
|
||||
|
||||
export function setTheme(theme: string = 'darkly') {
|
||||
|
|
2
ui/src/version.ts
vendored
2
ui/src/version.ts
vendored
|
@ -1 +1 @@
|
|||
export const version: string = 'v0.6.7';
|
||||
export const version: string = 'v0.6.10';
|
||||
|
|
2
ui/translation_report.ts
vendored
2
ui/translation_report.ts
vendored
|
@ -2,6 +2,7 @@ import { en } from './src/translations/en';
|
|||
import { eo } from './src/translations/eo';
|
||||
import { es } from './src/translations/es';
|
||||
import { de } from './src/translations/de';
|
||||
import { fa } from './src/translations/fa';
|
||||
import { zh } from './src/translations/zh';
|
||||
import { fr } from './src/translations/fr';
|
||||
import { sv } from './src/translations/sv';
|
||||
|
@ -15,6 +16,7 @@ import fs from 'fs';
|
|||
const files = [
|
||||
{ t: ca, n: 'ca' },
|
||||
{ t: de, n: 'de' },
|
||||
{ t: fa, n: 'fa' },
|
||||
{ t: eo, n: 'eo' },
|
||||
{ t: es, n: 'es' },
|
||||
{ t: fi, n: 'fi' },
|
||||
|
|
261
ui/yarn.lock
vendored
261
ui/yarn.lock
vendored
|
@ -9,10 +9,10 @@
|
|||
dependencies:
|
||||
"@babel/highlight" "^7.8.3"
|
||||
|
||||
"@babel/generator@^7.8.3":
|
||||
version "7.8.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.8.3.tgz#0e22c005b0a94c1c74eafe19ef78ce53a4d45c03"
|
||||
integrity sha512-WjoPk8hRpDRqqzRpvaR8/gDUPkrnOOeuT2m8cNICJtZH6mwaCo3v0OKMI7Y6SM1pBtyijnLtAL0HDi41pf41ug==
|
||||
"@babel/generator@^7.8.4":
|
||||
version "7.8.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.8.4.tgz#35bbc74486956fe4251829f9f6c48330e8d0985e"
|
||||
integrity sha512-PwhclGdRpNAf3IxZb0YVuITPZmmrXz9zf6fH8lT4XbrmfQKr6ryBzhv593P5C6poJRciFCL/eHGW2NuGrgEyxA==
|
||||
dependencies:
|
||||
"@babel/types" "^7.8.3"
|
||||
jsesc "^2.5.1"
|
||||
|
@ -51,23 +51,23 @@
|
|||
esutils "^2.0.2"
|
||||
js-tokens "^4.0.0"
|
||||
|
||||
"@babel/parser@^7.0.0", "@babel/parser@^7.8.3":
|
||||
version "7.8.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.3.tgz#790874091d2001c9be6ec426c2eed47bc7679081"
|
||||
integrity sha512-/V72F4Yp/qmHaTALizEm9Gf2eQHV3QyTL3K0cNfijwnMnb1L+LDlAubb/ZnSdGAVzVSWakujHYs1I26x66sMeQ==
|
||||
"@babel/parser@^7.0.0", "@babel/parser@^7.8.3", "@babel/parser@^7.8.4":
|
||||
version "7.8.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.4.tgz#d1dbe64691d60358a974295fa53da074dd2ce8e8"
|
||||
integrity sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==
|
||||
|
||||
"@babel/runtime-corejs3@^7.7.4":
|
||||
version "7.8.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.8.3.tgz#a2445836d0699e5ba77eea2c790ad9ea51e2cd27"
|
||||
integrity sha512-lrIU4aVbmlM/wQPzhEvzvNJskKyYptuXb0fGC0lTQTupTOYtR2Vqbu6/jf8vTr4M8Wt1nIzxVrSvPI5qESa/xA==
|
||||
version "7.8.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.8.4.tgz#ccc4e042e2fae419c67fa709567e5d2179ed3940"
|
||||
integrity sha512-+wpLqy5+fbQhvbllvlJEVRIpYj+COUWnnsm+I4jZlA8Lo7/MJmBhGTCHyk1/RWfOqBRJ2MbadddG6QltTKTlrg==
|
||||
dependencies:
|
||||
core-js-pure "^3.0.0"
|
||||
regenerator-runtime "^0.13.2"
|
||||
|
||||
"@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.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.3.tgz#0811944f73a6c926bb2ad35e918dcc1bfab279f1"
|
||||
integrity sha512-fVHx1rzEmwB130VTkLnxR+HmxcTjGzH12LYQcFFoBwakMd3aOMD4OsRN7tGG/UOYE2ektgFrS8uACAoRk1CY0w==
|
||||
version "7.8.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.4.tgz#d79f5a2040f7caa24d53e563aad49cbc05581308"
|
||||
integrity sha512-neAp3zt80trRVBI1x0azq6c57aNBqYZH8KhMm3TaB7wEI5Q4A2SHfBHE8w9gOhI/lrqxtEbXZgQIrHP+wvSGwQ==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.2"
|
||||
|
||||
|
@ -81,15 +81,15 @@
|
|||
"@babel/types" "^7.8.3"
|
||||
|
||||
"@babel/traverse@^7.0.0":
|
||||
version "7.8.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.8.3.tgz#a826215b011c9b4f73f3a893afbc05151358bf9a"
|
||||
integrity sha512-we+a2lti+eEImHmEXp7bM9cTxGzxPmBiVJlLVD+FuuQMeeO7RaDbutbgeheDkw+Xe3mCfJHnGOWLswT74m2IPg==
|
||||
version "7.8.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.8.4.tgz#f0845822365f9d5b0e312ed3959d3f827f869e3c"
|
||||
integrity sha512-NGLJPZwnVEyBPLI+bl9y9aSnxMhsKz42so7ApAv9D+b4vAFPpY013FTS9LdKxcABoIYFU52HcYga1pPlx454mg==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.8.3"
|
||||
"@babel/generator" "^7.8.3"
|
||||
"@babel/generator" "^7.8.4"
|
||||
"@babel/helper-function-name" "^7.8.3"
|
||||
"@babel/helper-split-export-declaration" "^7.8.3"
|
||||
"@babel/parser" "^7.8.3"
|
||||
"@babel/parser" "^7.8.4"
|
||||
"@babel/types" "^7.8.3"
|
||||
debug "^4.1.0"
|
||||
globals "^11.1.0"
|
||||
|
@ -169,10 +169,10 @@
|
|||
dependencies:
|
||||
"@types/linkify-it" "*"
|
||||
|
||||
"@types/node@^13.5.0":
|
||||
version "13.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.5.0.tgz#4e498dbf355795a611a87ae5ef811a8660d42662"
|
||||
integrity sha512-Onhn+z72D2O2Pb2ql2xukJ55rglumsVo1H6Fmyi8mlU9SvKdBk/pUSUAiBY/d9bAOF7VVWajX3sths/+g6ZiAQ==
|
||||
"@types/node@^13.7.0":
|
||||
version "13.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.7.0.tgz#b417deda18cf8400f278733499ad5547ed1abec4"
|
||||
integrity sha512-GnZbirvmqZUzMgkFn70c74OQpTTUcCzlhQliTzYjQMqg+hVKcDnxdL19Ne3UdYzdMA/+W3eb646FWn/ZaT1NfQ==
|
||||
|
||||
"@types/normalize-package-data@^2.4.0":
|
||||
version "2.4.0"
|
||||
|
@ -189,62 +189,40 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47"
|
||||
integrity sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==
|
||||
|
||||
"@typescript-eslint/eslint-plugin@2.16.0":
|
||||
version "2.16.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.16.0.tgz#bf339b7db824c7cc3fd1ebedbc88dd17016471af"
|
||||
integrity sha512-TKWbeFAKRPrvKiR9GNxErQ8sELKqg1ZvXi6uho07mcKShBnCnqNpDQWP01FEvWKf0bxM2g7uQEI5MNjSNqvUpQ==
|
||||
"@typescript-eslint/eslint-plugin@2.18.0":
|
||||
version "2.18.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.18.0.tgz#f8cf272dfb057ecf1ea000fea1e0b3f06a32f9cb"
|
||||
integrity sha512-kuO8WQjV+RCZvAXVRJfXWiJ8iYEtfHlKgcqqqXg9uUkIolEHuUaMmm8/lcO4xwCOtaw6mY0gStn2Lg4/eUXXYQ==
|
||||
dependencies:
|
||||
"@typescript-eslint/experimental-utils" "2.16.0"
|
||||
"@typescript-eslint/experimental-utils" "2.18.0"
|
||||
eslint-utils "^1.4.3"
|
||||
functional-red-black-tree "^1.0.1"
|
||||
regexpp "^3.0.0"
|
||||
tsutils "^3.17.1"
|
||||
|
||||
"@typescript-eslint/experimental-utils@2.16.0":
|
||||
version "2.16.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.16.0.tgz#bba65685728c532e0ddc811a0376e8d38e671f77"
|
||||
integrity sha512-bXTmAztXpqxliDKZgvWkl+5dHeRN+jqXVZ16peKKFzSXVzT6mz8kgBpHiVzEKO2NZ8OCU7dG61K9sRS/SkUUFQ==
|
||||
"@typescript-eslint/experimental-utils@2.18.0", "@typescript-eslint/experimental-utils@^2.5.0":
|
||||
version "2.18.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.18.0.tgz#e4eab839082030282496c1439bbf9fdf2a4f3da8"
|
||||
integrity sha512-J6MopKPHuJYmQUkANLip7g9I82ZLe1naCbxZZW3O2sIxTiq/9YYoOELEKY7oPg0hJ0V/AQ225h2z0Yp+RRMXhw==
|
||||
dependencies:
|
||||
"@types/json-schema" "^7.0.3"
|
||||
"@typescript-eslint/typescript-estree" "2.16.0"
|
||||
"@typescript-eslint/typescript-estree" "2.18.0"
|
||||
eslint-scope "^5.0.0"
|
||||
|
||||
"@typescript-eslint/experimental-utils@^2.5.0":
|
||||
version "2.17.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.17.0.tgz#12ed4a5d656e02ff47a93efc7d1ce1b8f1242351"
|
||||
integrity sha512-2bNf+mZ/3mj5/3CP56v+ldRK3vFy9jOvmCPs/Gr2DeSJh+asPZrhFniv4QmQsHWQFPJFWhFHgkGgJeRmK4m8iQ==
|
||||
dependencies:
|
||||
"@types/json-schema" "^7.0.3"
|
||||
"@typescript-eslint/typescript-estree" "2.17.0"
|
||||
eslint-scope "^5.0.0"
|
||||
|
||||
"@typescript-eslint/parser@2.16.0":
|
||||
version "2.16.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.16.0.tgz#d0c0135a8fdb915f670802ddd7c1ba457c1b4f9d"
|
||||
integrity sha512-+w8dMaYETM9v6il1yYYkApMSiwgnqXWJbXrA94LAWN603vXHACsZTirJduyeBOJjA9wT6xuXe5zZ1iCUzoxCfw==
|
||||
"@typescript-eslint/parser@2.18.0":
|
||||
version "2.18.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.18.0.tgz#d5f7fc1839abd4a985394e40e9d2454bd56aeb1f"
|
||||
integrity sha512-SJJPxFMEYEWkM6pGfcnjLU+NJIPo+Ko1QrCBL+i0+zV30ggLD90huEmMMhKLHBpESWy9lVEeWlQibweNQzyc+A==
|
||||
dependencies:
|
||||
"@types/eslint-visitor-keys" "^1.0.0"
|
||||
"@typescript-eslint/experimental-utils" "2.16.0"
|
||||
"@typescript-eslint/typescript-estree" "2.16.0"
|
||||
"@typescript-eslint/experimental-utils" "2.18.0"
|
||||
"@typescript-eslint/typescript-estree" "2.18.0"
|
||||
eslint-visitor-keys "^1.1.0"
|
||||
|
||||
"@typescript-eslint/typescript-estree@2.16.0":
|
||||
version "2.16.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.16.0.tgz#b444943a76c716ed32abd08cbe96172d2ca0ab75"
|
||||
integrity sha512-hyrCYjFHISos68Bk5KjUAXw0pP/455qq9nxqB1KkT67Pxjcfw+r6Yhcmqnp8etFL45UexCHUMrADHH7dI/m2WQ==
|
||||
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"
|
||||
|
||||
"@typescript-eslint/typescript-estree@2.17.0":
|
||||
version "2.17.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.17.0.tgz#2ce1531ec0925ef8d22d7026235917c2638a82af"
|
||||
integrity sha512-g0eVRULGnEEUakxRfJO0s0Hr1LLQqsI6OrkiCLpdHtdJJek+wyd8mb00vedqAoWldeDcOcP8plqw8/jx9Gr3Lw==
|
||||
"@typescript-eslint/typescript-estree@2.18.0":
|
||||
version "2.18.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.18.0.tgz#cfbd16ed1b111166617d718619c19b62764c8460"
|
||||
integrity sha512-gVHylf7FDb8VSi2ypFuEL3hOtoC4HkZZ5dOjXvVjoyKdRrvXAOPSzpNRnKMfaUUEiSLP8UF9j9X9EDLxC0lfZg==
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
eslint-visitor-keys "^1.1.0"
|
||||
|
@ -605,9 +583,9 @@ bootswatch@^4.3.1:
|
|||
integrity sha512-Kx3z6+3Jpg9g6l/xZBCnc8d6KeJK0QawxCZWOomdcI5AuSZLZb+DoH5X9RJH+cOcSeMAxyzdIjkVUR01+Db5bQ==
|
||||
|
||||
bowser@^2.0.0-beta.3:
|
||||
version "2.8.1"
|
||||
resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.8.1.tgz#35b74165e17b80ba8af6aa4736c2861b001fc09e"
|
||||
integrity sha512-FxxltGKqMHkVa3KtpA+kdnxH0caHPDewccyrK3vW1bsMw6Zco4vRPmMunowX0pXlDZqhxkKSpToADQI2Sk4OeQ==
|
||||
version "2.9.0"
|
||||
resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.9.0.tgz#3bed854233b419b9a7422d9ee3e85504373821c9"
|
||||
integrity sha512-2ld76tuLBNFekRgmJfT2+3j5MIrP6bFict8WAIT3beq+srz1gcKNAdNKMqHqauQt63NmAa88HfP1/Ypa9Er3HA==
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.11"
|
||||
|
@ -769,9 +747,9 @@ classcat@^1.1.3:
|
|||
integrity sha512-nuf6HJ5RlEgUUPqN/giIy1wsfA0LJwCHpo/aMGMwEIAxYypbLW/ZdPH4SNrF+OwdrkL3wxJmAs4GPyoE3ZkQ4w==
|
||||
|
||||
clean-css@^4.1.9:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.1.tgz#2d411ef76b8569b6d0c84068dabe85b0aa5e5c17"
|
||||
integrity sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz#507b5de7d97b48ee53d84adb0160ff6216380f78"
|
||||
integrity sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==
|
||||
dependencies:
|
||||
source-map "~0.6.0"
|
||||
|
||||
|
@ -952,9 +930,9 @@ cross-spawn@^7.0.0:
|
|||
which "^2.0.1"
|
||||
|
||||
damerau-levenshtein@^1.0.4:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.5.tgz#780cf7144eb2e8dbd1c3bb83ae31100ccc31a414"
|
||||
integrity sha512-CBCRqFnpu715iPmw1KrdOrzRqbdFwQTwAWyyyYS42+iAgHCuXZ+/TdMgQkUENPomxEz9z1BEzuQU2Xw0kUuAgA==
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz#143c1641cb3d85c60c32329e26899adea8701791"
|
||||
integrity sha512-JVrozIeElnj3QzfUIt8tB8YMluBJom4Vw9qTPpjGYQ9fYlB3D/rb6OordUxf3xeFB35LKWs0xqcO5U6ySvBtug==
|
||||
|
||||
dashdash@^1.12.0:
|
||||
version "1.14.1"
|
||||
|
@ -1102,9 +1080,9 @@ emoji-regex@^8.0.0:
|
|||
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
|
||||
|
||||
emoji-short-name@^0.1.0:
|
||||
version "0.1.3"
|
||||
resolved "https://registry.yarnpkg.com/emoji-short-name/-/emoji-short-name-0.1.3.tgz#7cedc74b599483ba2bee8d3b3241299f4fdf924f"
|
||||
integrity sha512-Z9pe0l664P+mVh9C1+l45KSc8+nMNY43Hc1jltGPdGVpCnScvam+CZu2hl+xynxB0oqvghiSFanJhuUFhb1uYQ==
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/emoji-short-name/-/emoji-short-name-0.1.4.tgz#125a452adc22a399b089f802f9d8d46ecb6e5b08"
|
||||
integrity sha512-VTjEKkhN1UARtHLqlK70N5K3SwxuZAkmdm5sXvSjkV677kr0jt/O7mvB5eQqM+3rKCa+w3Qb5G7wwU/fezonKQ==
|
||||
|
||||
encodeurl@~1.0.2:
|
||||
version "1.0.2"
|
||||
|
@ -1186,10 +1164,10 @@ eslint-ast-utils@^1.1.0:
|
|||
lodash.get "^4.4.2"
|
||||
lodash.zip "^4.2.0"
|
||||
|
||||
eslint-config-prettier@6.9.0:
|
||||
version "6.9.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.9.0.tgz#430d24822e82f7deb1e22a435bfa3999fae4ad64"
|
||||
integrity sha512-k4E14HBtcLv0uqThaI6I/n1LEqROp8XaPu6SO9Z32u5NlGRC07Enu1Bh2KEFw4FNHbekH8yzbIU9kUGxbiGmCA==
|
||||
eslint-config-prettier@6.10.0:
|
||||
version "6.10.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.10.0.tgz#7b15e303bf9c956875c948f6b21500e48ded6a7f"
|
||||
integrity sha512-AtndijGte1rPILInUdHjvKEGbIV06NuvPrqlIEaEaWtbtvJh464mDeyGMdZEQMsGvC0ZVkiex1fSNcC4HAbRGg==
|
||||
dependencies:
|
||||
get-stdin "^6.0.0"
|
||||
|
||||
|
@ -1257,14 +1235,14 @@ eslint-plugin-inferno@^7.14.3:
|
|||
resolve "^1.12.0"
|
||||
|
||||
eslint-plugin-jane@^7.0.2:
|
||||
version "7.0.2"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-jane/-/eslint-plugin-jane-7.0.2.tgz#e6c6e402c95d87630f739ae7cca3837c32419757"
|
||||
integrity sha512-kVSIwAbwo8CFKwpLSzAeyT1izM2WUCgOrIiZQSX7dNwfqYh7Utl4rhUeBB0ItzRV8C+YeRTVZ742XqaFnJPIxw==
|
||||
version "7.1.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-jane/-/eslint-plugin-jane-7.1.0.tgz#ee087405329e6bc9bfe9316fc5881c1d4e27bc71"
|
||||
integrity sha512-ScsxkkeTUnGYKLaiIk5zz/x7ZkDh7+rTj94daZboNmkJejdYka0sLFpfvDGm/7B8ImKacKdjRatQD0HjxlaPzA==
|
||||
dependencies:
|
||||
"@typescript-eslint/eslint-plugin" "2.16.0"
|
||||
"@typescript-eslint/parser" "2.16.0"
|
||||
"@typescript-eslint/eslint-plugin" "2.18.0"
|
||||
"@typescript-eslint/parser" "2.18.0"
|
||||
babel-eslint "10.0.3"
|
||||
eslint-config-prettier "6.9.0"
|
||||
eslint-config-prettier "6.10.0"
|
||||
eslint-plugin-babel "5.3.0"
|
||||
eslint-plugin-import "2.20.0"
|
||||
eslint-plugin-jest "23.6.0"
|
||||
|
@ -2175,9 +2153,9 @@ husky@^4.2.1:
|
|||
which-pm-runs "^1.0.0"
|
||||
|
||||
i18next@^19.0.3:
|
||||
version "19.0.3"
|
||||
resolved "https://registry.yarnpkg.com/i18next/-/i18next-19.0.3.tgz#31fd3165762d9802e08a2a86932db4eff5c862e9"
|
||||
integrity sha512-Ru4afr++b4cUApsIBifcMYyWG9Nx8wlFdq4DuOF+UuoPoQKfuh0iAVMekTjs6w1CZLUOVb5QZEuoYRLmu17EIA==
|
||||
version "19.1.0"
|
||||
resolved "https://registry.yarnpkg.com/i18next/-/i18next-19.1.0.tgz#fe1a1da3d208872946307c7d2d115da45d46159f"
|
||||
integrity sha512-ISbmukX4L6Dz0QoH9+EW1AnBw7j+NRLoMu9uLPMaNSSTP9Eie9/oUL0dOyWX15baB3gYOpkHJpGZRHOqcnl0ew==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
|
||||
|
@ -2227,18 +2205,18 @@ indent-string@^3.0.0:
|
|||
integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=
|
||||
|
||||
inferno-clone-vnode@^7.1.12:
|
||||
version "7.3.3"
|
||||
resolved "https://registry.yarnpkg.com/inferno-clone-vnode/-/inferno-clone-vnode-7.3.3.tgz#54815f8e48195b2ed4c301a4a4df0e16ecb772ea"
|
||||
integrity sha512-kuUO7wWuQ3ktxRHXPpYejleJrs2XieOum4GW8WcW8ZugJj6gVnuE4pHMomiC59w6yqDacxqawX7oOWZ/K7qW5g==
|
||||
version "7.4.0"
|
||||
resolved "https://registry.yarnpkg.com/inferno-clone-vnode/-/inferno-clone-vnode-7.4.0.tgz#44a930ef0881f79d425c1c7f4bbd206513da905a"
|
||||
integrity sha512-rPp4tMhWZB1H2kx0MqgyPPBP4bWIXwkH+E/eNSWWtXLR5mKDGz19cguiBkR+U1uXQCi4/AkWvOVHxLQCfT/5Zw==
|
||||
dependencies:
|
||||
inferno "7.3.3"
|
||||
inferno "7.4.0"
|
||||
|
||||
inferno-create-element@^7.1.12:
|
||||
version "7.3.3"
|
||||
resolved "https://registry.yarnpkg.com/inferno-create-element/-/inferno-create-element-7.3.3.tgz#6a53084fad9689cb94aa40aede65ab15f690401a"
|
||||
integrity sha512-96kUD6uQFESCvWr7tud6/AA1xhQgv/qT6HQU/vBPS9xB9HwEoLfi4eTTBB6igpTBpeEPuHRE2jMhELVP1yJapQ==
|
||||
version "7.4.0"
|
||||
resolved "https://registry.yarnpkg.com/inferno-create-element/-/inferno-create-element-7.4.0.tgz#b431f293cdb8931f7f3604e0774500b66d6fe5c8"
|
||||
integrity sha512-gxwU899obmELIxfhWzyHBIGbxOXUPfB1SzW+K3XGU0exWKCVIJwSpBOGpJY5tlKf4lyg1UrCmfz2JZS1i2U2vg==
|
||||
dependencies:
|
||||
inferno "7.3.3"
|
||||
inferno "7.4.0"
|
||||
|
||||
inferno-i18next@nimbusec-oss/inferno-i18next:
|
||||
version "7.1.12"
|
||||
|
@ -2252,32 +2230,32 @@ inferno-i18next@nimbusec-oss/inferno-i18next:
|
|||
inferno-vnode-flags "^7.1.12"
|
||||
|
||||
inferno-router@^7.0.1:
|
||||
version "7.3.3"
|
||||
resolved "https://registry.yarnpkg.com/inferno-router/-/inferno-router-7.3.3.tgz#8b7b2a5bdf1a91c31dac3053e63e622e70c520bb"
|
||||
integrity sha512-l7lBluSGnYPX0nmQ7OcM978LCWRMFpSJ0D6Lx1Ri4wbGk2+DpycyOQ0dKY3uhRumoN1j8jTBJFfePr9D5NvDsQ==
|
||||
version "7.4.0"
|
||||
resolved "https://registry.yarnpkg.com/inferno-router/-/inferno-router-7.4.0.tgz#0af6b931c58f426d0d7e7754d51a51300882364a"
|
||||
integrity sha512-6Q76UjAiPd1mO/5sbDaEoEN9MdMHKkEXnYNOZ02sSudj5jWCFzJ/JnSF526uNxAHQpw2DKCh2pNiu6qf/b1vQQ==
|
||||
dependencies:
|
||||
history "^4.10.1"
|
||||
hoist-non-inferno-statics "^1.1.3"
|
||||
inferno "7.3.3"
|
||||
inferno "7.4.0"
|
||||
path-to-regexp-es6 "1.7.0"
|
||||
|
||||
inferno-shared@7.3.3, inferno-shared@^7.1.12:
|
||||
version "7.3.3"
|
||||
resolved "https://registry.yarnpkg.com/inferno-shared/-/inferno-shared-7.3.3.tgz#aa4b70a38d1f37498766f31c6a99f5c5dfc58b63"
|
||||
integrity sha512-OPpYFEHLA6grY8phbdG21ST7mjkUNXjZMpfZKgHrPUORFxnnn+u+i57QDpht5RtUZgVpHIKNxNQypq6+/m4LEA==
|
||||
inferno-shared@7.4.0, inferno-shared@^7.1.12:
|
||||
version "7.4.0"
|
||||
resolved "https://registry.yarnpkg.com/inferno-shared/-/inferno-shared-7.4.0.tgz#4491deb75348019939b160cd5655196afa13ced0"
|
||||
integrity sha512-6aa1fC/e4SP2lOLNg4ZS5Zz2SC+DnM7WxQbggmHhLSyOqZrsPrpZSlX25LbjR9lkhMrq6cmki3yInYFGuDzlRg==
|
||||
|
||||
inferno-vnode-flags@7.3.3, inferno-vnode-flags@^7.1.12:
|
||||
version "7.3.3"
|
||||
resolved "https://registry.yarnpkg.com/inferno-vnode-flags/-/inferno-vnode-flags-7.3.3.tgz#aebaddea1569dd16512f44b92bf587837328db9d"
|
||||
integrity sha512-LzLIRVrpv3OoH5gwWXOrHmgx3vMysI1fEG9PUBEc7Alz+vnD9rRBu9sP5AvGRN7Nxli7iLo6WcqF1nDIANGL7Q==
|
||||
inferno-vnode-flags@7.4.0, inferno-vnode-flags@^7.1.12:
|
||||
version "7.4.0"
|
||||
resolved "https://registry.yarnpkg.com/inferno-vnode-flags/-/inferno-vnode-flags-7.4.0.tgz#5c049a73f3ff84a51458b06d279d6b18d09acdf0"
|
||||
integrity sha512-TMPrvAxR2uUVSowLKnGgH34eWXErIYCdJ4d5hj8cSc8ta8knN6dj0z47UIw13qvmWfNjHgwm0C2/cm+G6fckiA==
|
||||
|
||||
inferno@7.3.3, inferno@^7.0.1, inferno@^7.1.12:
|
||||
version "7.3.3"
|
||||
resolved "https://registry.yarnpkg.com/inferno/-/inferno-7.3.3.tgz#4098d5313c53281e44a857619764e74ab4438415"
|
||||
integrity sha512-FlTMi77+uF0dR3HDfrhysCmOPE6cj9/2jDLQzUSx0KciewVQq7N2KdsfsA0HVSzVb9Do1pjcRtnAIDXmfKzGfA==
|
||||
inferno@7.4.0, inferno@^7.0.1, inferno@^7.1.12:
|
||||
version "7.4.0"
|
||||
resolved "https://registry.yarnpkg.com/inferno/-/inferno-7.4.0.tgz#8d3dc03562c6851043a1a467fd509f222e9dbf85"
|
||||
integrity sha512-oEXx5iQmGXOvAPj1TZyCo6ndOc4qPg9zBLigMpkApAiV1SM/bri0M1eA/kD3e9jptcof9TwLBJD9bL6E6tq2tg==
|
||||
dependencies:
|
||||
inferno-shared "7.3.3"
|
||||
inferno-vnode-flags "7.3.3"
|
||||
inferno-shared "7.4.0"
|
||||
inferno-vnode-flags "7.4.0"
|
||||
opencollective-postinstall "^2.0.2"
|
||||
|
||||
inflight@^1.0.4:
|
||||
|
@ -2319,9 +2297,9 @@ inquirer@^3.0.6:
|
|||
through "^2.3.6"
|
||||
|
||||
inquirer@^7.0.0:
|
||||
version "7.0.3"
|
||||
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.0.3.tgz#f9b4cd2dff58b9f73e8d43759436ace15bed4567"
|
||||
integrity sha512-+OiOVeVydu4hnCGLCSX+wedovR/Yzskv9BFqUNNKq9uU2qg7LCcCo3R86S2E7WLo0y/x2pnEZfZe1CoYnORUAw==
|
||||
version "7.0.4"
|
||||
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.0.4.tgz#99af5bde47153abca23f5c7fc30db247f39da703"
|
||||
integrity sha512-Bu5Td5+j11sCkqfqmUTiwv+tWisMtP0L7Q8WrqA2C/BbBhy1YTdFrvjjlrKq8oagA/tLQBski2Gcx/Sqyi2qSQ==
|
||||
dependencies:
|
||||
ansi-escapes "^4.2.1"
|
||||
chalk "^2.4.2"
|
||||
|
@ -2763,9 +2741,9 @@ linkify-it@^2.0.0:
|
|||
uc.micro "^1.0.1"
|
||||
|
||||
lint-staged@^10.0.2:
|
||||
version "10.0.2"
|
||||
resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.0.2.tgz#cfdd9fa5080b05fc6e29536897da1795bc67c7f9"
|
||||
integrity sha512-ZldhtIfT7bynVa7nmU/1jbK05r9hYQXbIQqZSotqdBCAcGJDEUqaUB7kG3ZCdoe9Qkj6HUM3x2yjCGJRxPUQLA==
|
||||
version "10.0.7"
|
||||
resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.0.7.tgz#d205f92d9359419a23bc6aa3b6f8546b1998da64"
|
||||
integrity sha512-Byj0F4l7GYUpYYHEqyFH69NiI6ICTg0CeCKbhRorL+ickbzILKUlZLiyCkljZV02wnoh7yH7PmFyYm9PRNwk9g==
|
||||
dependencies:
|
||||
chalk "^3.0.0"
|
||||
commander "^4.0.1"
|
||||
|
@ -3753,6 +3731,11 @@ realm-utils@^1.0.9:
|
|||
app-root-path "^1.3.0"
|
||||
mkdirp "^0.5.1"
|
||||
|
||||
reconnecting-websocket@^4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/reconnecting-websocket/-/reconnecting-websocket-4.3.0.tgz#aaefbc7629a89450aa45324b89aec2276e728cc5"
|
||||
integrity sha512-3eaHIEVYB9Zb0GfYy1xdEHKJLA2JaawAegByZ1AZ8Npb3AiRgUN5l89cvE2H+pHTsFcoC88t32ky9qET6DJ75Q==
|
||||
|
||||
regenerate-unicode-properties@^8.1.0:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e"
|
||||
|
@ -3786,9 +3769,9 @@ regex-not@^1.0.0, regex-not@^1.0.2:
|
|||
safe-regex "^1.1.0"
|
||||
|
||||
regexp-tree@^0.1.17, regexp-tree@~0.1.1:
|
||||
version "0.1.17"
|
||||
resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.17.tgz#66d914a6ca21f95dd7660ed70a7dad47aeb2246a"
|
||||
integrity sha512-UnOJjFS/EPZmfISmYx+0PcDtPzyFKTe+cZTS5sM5hifnRUDRxoB1j4DAmGwqzxjwBGlwOkGfb2cDGHtjuEwqoA==
|
||||
version "0.1.18"
|
||||
resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.18.tgz#ed4819a9f03ec2de9613421d6eaf47512e7fdaf1"
|
||||
integrity sha512-mKLUfTDU1GE5jGR7cn2IEPDzYjmOviZOHYAR1XGe8Lg48Mdk684waD1Fqhv2Nef+TsDVdmIj08m/GUKTMk7J2Q==
|
||||
|
||||
regexpp@^2.0.1:
|
||||
version "2.0.1"
|
||||
|
@ -4123,9 +4106,9 @@ snapdragon@^0.8.1:
|
|||
use "^3.1.0"
|
||||
|
||||
sortpack@^2.0.1:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/sortpack/-/sortpack-2.0.4.tgz#ca537fbf461351795eee5c2be483ee57e5664c69"
|
||||
integrity sha512-XwtYcxATWJTBWjCWakakFzDqeBqdG5XS0iyzCfOl2KznAOV1YWFzaSf9QQuedZ2i78VHF7Ix1RscrKJ9Dlcm0w==
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/sortpack/-/sortpack-2.1.1.tgz#e94280616a517851257728721dd6749619aca309"
|
||||
integrity sha512-/jtQAzl9JeTXZxzznW6L729M+Q7uv9k9Dm89eF0UxMj4Rna3CmO0IYT0MUS6aLyHUOTnwpT7kIDs4PQmMTEhLw==
|
||||
|
||||
source-map-resolve@^0.5.0:
|
||||
version "0.5.3"
|
||||
|
@ -4416,9 +4399,9 @@ through@^2.3.6:
|
|||
integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
|
||||
|
||||
tiny-invariant@^1.0.2:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.0.6.tgz#b3f9b38835e36a41c843a3b0907a5a7b3755de73"
|
||||
integrity sha512-FOyLWWVjG+aC0UqG76V53yAWdXfH8bO6FNmyZOuUrzDzK8DI3/JRY25UD7+g49JWM1LXwymsKERB+DzI0dTEQA==
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875"
|
||||
integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==
|
||||
|
||||
tiny-warning@^1.0.0:
|
||||
version "1.0.3"
|
||||
|
@ -4539,19 +4522,19 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0:
|
|||
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
|
||||
integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
|
||||
|
||||
twemoji-parser@12.1.1:
|
||||
version "12.1.1"
|
||||
resolved "https://registry.yarnpkg.com/twemoji-parser/-/twemoji-parser-12.1.1.tgz#9532a869e3348dbb129d93ed1850f6e871bec2be"
|
||||
integrity sha512-XFUB4ReEvPbNPtiuyo/+crM4RldYbRRAhyE7Hw6EnfBdXECGydw7a49EGADayRvaeierP/m4DSv/OZQObh0LGA==
|
||||
twemoji-parser@12.1.3:
|
||||
version "12.1.3"
|
||||
resolved "https://registry.yarnpkg.com/twemoji-parser/-/twemoji-parser-12.1.3.tgz#916c0153e77bd5f1011e7a99cbeacf52e43c9371"
|
||||
integrity sha512-ND4LZXF4X92/PFrzSgGkq6KPPg8swy/U0yRw1k/+izWRVmq1HYi3khPwV3XIB6FRudgVICAaBhJfW8e8G3HC7Q==
|
||||
|
||||
twemoji@^12.1.2:
|
||||
version "12.1.4"
|
||||
resolved "https://registry.yarnpkg.com/twemoji/-/twemoji-12.1.4.tgz#bf61470cc70f9c18fa5c212de1fe2637cd159589"
|
||||
integrity sha512-e37lUlVijmABF7wPCc09s1kKj3hcpzU8KL5zw2bBDIXOtOr4luLF+ODJaEqca8dZPmLR5ezrJYI93nhPovKBiQ==
|
||||
version "12.1.5"
|
||||
resolved "https://registry.yarnpkg.com/twemoji/-/twemoji-12.1.5.tgz#a961fb65a1afcb1f729ad7e59391f9fe969820b9"
|
||||
integrity sha512-B0PBVy5xomwb1M/WZxf/IqPZfnoIYy1skXnlHjMwLwTNfZ9ljh8VgWQktAPcJXu8080WoEh6YwQGPVhDVqvrVQ==
|
||||
dependencies:
|
||||
fs-extra "^8.0.1"
|
||||
jsonfile "^5.0.0"
|
||||
twemoji-parser "12.1.1"
|
||||
twemoji-parser "12.1.3"
|
||||
universalify "^0.1.2"
|
||||
|
||||
type-check@~0.3.2:
|
||||
|
|
Loading…
Reference in a new issue