mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-11-30 00:01:25 +00:00
Merge branch 'dev'
This commit is contained in:
commit
ebfdd385ef
37 changed files with 623 additions and 130 deletions
|
@ -37,6 +37,7 @@ Front Page|Post
|
||||||
- Can ban and unban users from communities and the site.
|
- Can ban and unban users from communities and the site.
|
||||||
- Clean, mobile-friendly interface.
|
- Clean, mobile-friendly interface.
|
||||||
- i18n / internationalization support.
|
- i18n / internationalization support.
|
||||||
|
- NSFW post / community support.
|
||||||
- High performance.
|
- High performance.
|
||||||
- Server is written in rust.
|
- Server is written in rust.
|
||||||
- Front end is `~80kB` gzipped.
|
- Front end is `~80kB` gzipped.
|
||||||
|
|
|
@ -2,12 +2,14 @@ version: '2.4'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
db:
|
db:
|
||||||
image: postgres
|
image: postgres:12-alpine
|
||||||
restart: always
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_USER: rrr
|
POSTGRES_USER: rrr
|
||||||
POSTGRES_PASSWORD: rrr
|
POSTGRES_PASSWORD: rrr
|
||||||
POSTGRES_DB: rrr
|
POSTGRES_DB: rrr
|
||||||
|
volumes:
|
||||||
|
- db:/var/lib/postgresql/data
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "pg_isready -U rrr"]
|
test: ["CMD-SHELL", "pg_isready -U rrr"]
|
||||||
interval: 5s
|
interval: 5s
|
||||||
|
@ -27,3 +29,5 @@ services:
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
volumes:
|
||||||
|
db:
|
||||||
|
|
|
@ -2,12 +2,14 @@ version: '2.4'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
db:
|
db:
|
||||||
image: postgres
|
image: postgres:12-alpine
|
||||||
restart: always
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_USER: rrr
|
POSTGRES_USER: rrr
|
||||||
POSTGRES_PASSWORD: rrr
|
POSTGRES_PASSWORD: rrr
|
||||||
POSTGRES_DB: rrr
|
POSTGRES_DB: rrr
|
||||||
|
volumes:
|
||||||
|
- db:/var/lib/postgresql/data
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "pg_isready -U rrr"]
|
test: ["CMD-SHELL", "pg_isready -U rrr"]
|
||||||
interval: 5s
|
interval: 5s
|
||||||
|
@ -26,3 +28,5 @@ services:
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
volumes:
|
||||||
|
db:
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
docker exec -it lemmy_db_1 pg_dumpall -c -U rrr > dump_`date +%d-%m-%Y"_"%H_%M_%S`.sql
|
docker exec -it lemmy_db_1 pg_dumpall -c -U rrr > dump_`date +%Y-%m-%d"_"%H_%M_%S`.sql
|
||||||
|
|
18
docs/api.md
18
docs/api.md
|
@ -28,7 +28,7 @@ A simple test command:
|
||||||
|
|
||||||
## API
|
## API
|
||||||
### List
|
### List
|
||||||
`Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, SaveComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, SavePost, EditCommunity, FollowCommunity, GetFollowedCommunities, GetUserDetails, GetReplies, GetModlog, BanFromCommunity, AddModToCommunity, CreateSite, EditSite, GetSite, AddAdmin, BanUser, Search, MarkAllAsRead`
|
`Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, SaveComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, SavePost, EditCommunity, FollowCommunity, GetFollowedCommunities, GetUserDetails, GetReplies, GetModlog, BanFromCommunity, AddModToCommunity, CreateSite, EditSite, GetSite, AddAdmin, BanUser, Search, MarkAllAsRead, SaveUserSettings`
|
||||||
|
|
||||||
### Sort Types
|
### Sort Types
|
||||||
These go wherever there is a `sort` field.
|
These go wherever there is a `sort` field.
|
||||||
|
@ -109,7 +109,21 @@ Only the first user will be able to be the admin.
|
||||||
posts: Vec<PostView>,
|
posts: Vec<PostView>,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
#### Save User Settings
|
||||||
|
##### Request
|
||||||
|
```rust
|
||||||
|
{
|
||||||
|
show_nsfw: bool,
|
||||||
|
auth: String,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
##### Response
|
||||||
|
```rust
|
||||||
|
{
|
||||||
|
op: String,
|
||||||
|
jwt: String
|
||||||
|
}
|
||||||
|
```
|
||||||
#### Get Replies / Inbox
|
#### Get Replies / Inbox
|
||||||
##### Request
|
##### Request
|
||||||
```rust
|
```rust
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
drop view community_view;
|
||||||
|
drop view post_view;
|
||||||
|
alter table community drop column nsfw;
|
||||||
|
alter table post drop column nsfw;
|
||||||
|
alter table user_ drop column show_nsfw;
|
||||||
|
|
||||||
|
-- the views
|
||||||
|
create view community_view as
|
||||||
|
with all_community as
|
||||||
|
(
|
||||||
|
select *,
|
||||||
|
(select name from user_ u where c.creator_id = u.id) as creator_name,
|
||||||
|
(select name from category ct where c.category_id = ct.id) as category_name,
|
||||||
|
(select count(*) from community_follower cf where cf.community_id = c.id) as number_of_subscribers,
|
||||||
|
(select count(*) from post p where p.community_id = c.id) as number_of_posts,
|
||||||
|
(select count(*) from comment co, post p where c.id = p.community_id and p.id = co.post_id) as number_of_comments,
|
||||||
|
hot_rank((select count(*) from community_follower cf where cf.community_id = c.id), c.published) as hot_rank
|
||||||
|
from community c
|
||||||
|
)
|
||||||
|
|
||||||
|
select
|
||||||
|
ac.*,
|
||||||
|
u.id as user_id,
|
||||||
|
(select cf.id::boolean from community_follower cf where u.id = cf.user_id and ac.id = cf.community_id) as subscribed
|
||||||
|
from user_ u
|
||||||
|
cross join all_community ac
|
||||||
|
|
||||||
|
union all
|
||||||
|
|
||||||
|
select
|
||||||
|
ac.*,
|
||||||
|
null as user_id,
|
||||||
|
null as subscribed
|
||||||
|
from all_community ac
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
-- Post view
|
||||||
|
create view post_view as
|
||||||
|
with all_post as
|
||||||
|
(
|
||||||
|
select
|
||||||
|
p.*,
|
||||||
|
(select name from user_ where p.creator_id = user_.id) as creator_name,
|
||||||
|
(select name from community where p.community_id = community.id) as community_name,
|
||||||
|
(select removed from community c where p.community_id = c.id) as community_removed,
|
||||||
|
(select deleted from community c where p.community_id = c.id) as community_deleted,
|
||||||
|
(select count(*) from comment where comment.post_id = p.id) as number_of_comments,
|
||||||
|
coalesce(sum(pl.score), 0) as score,
|
||||||
|
count (case when pl.score = 1 then 1 else null end) as upvotes,
|
||||||
|
count (case when pl.score = -1 then 1 else null end) as downvotes,
|
||||||
|
hot_rank(coalesce(sum(pl.score) , 0), p.published) as hot_rank
|
||||||
|
from post p
|
||||||
|
left join post_like pl on p.id = pl.post_id
|
||||||
|
group by p.id
|
||||||
|
)
|
||||||
|
|
||||||
|
select
|
||||||
|
ap.*,
|
||||||
|
u.id as user_id,
|
||||||
|
coalesce(pl.score, 0) as my_vote,
|
||||||
|
(select cf.id::bool from community_follower cf where u.id = cf.user_id and cf.community_id = ap.community_id) as subscribed,
|
||||||
|
(select pr.id::bool from post_read pr where u.id = pr.user_id and pr.post_id = ap.id) as read,
|
||||||
|
(select ps.id::bool from post_saved ps where u.id = ps.user_id and ps.post_id = ap.id) as saved
|
||||||
|
from user_ u
|
||||||
|
cross join all_post ap
|
||||||
|
left join post_like pl on u.id = pl.user_id and ap.id = pl.post_id
|
||||||
|
|
||||||
|
union all
|
||||||
|
|
||||||
|
select
|
||||||
|
ap.*,
|
||||||
|
null as user_id,
|
||||||
|
null as my_vote,
|
||||||
|
null as subscribed,
|
||||||
|
null as read,
|
||||||
|
null as saved
|
||||||
|
from all_post ap
|
||||||
|
;
|
||||||
|
|
79
server/migrations/2019-08-11-000918_add_nsfw_columns/up.sql
Normal file
79
server/migrations/2019-08-11-000918_add_nsfw_columns/up.sql
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
alter table community add column nsfw boolean default false not null;
|
||||||
|
alter table post add column nsfw boolean default false not null;
|
||||||
|
alter table user_ add column show_nsfw boolean default false not null;
|
||||||
|
|
||||||
|
-- The views
|
||||||
|
drop view community_view;
|
||||||
|
create view community_view as
|
||||||
|
with all_community as
|
||||||
|
(
|
||||||
|
select *,
|
||||||
|
(select name from user_ u where c.creator_id = u.id) as creator_name,
|
||||||
|
(select name from category ct where c.category_id = ct.id) as category_name,
|
||||||
|
(select count(*) from community_follower cf where cf.community_id = c.id) as number_of_subscribers,
|
||||||
|
(select count(*) from post p where p.community_id = c.id) as number_of_posts,
|
||||||
|
(select count(*) from comment co, post p where c.id = p.community_id and p.id = co.post_id) as number_of_comments,
|
||||||
|
hot_rank((select count(*) from community_follower cf where cf.community_id = c.id), c.published) as hot_rank
|
||||||
|
from community c
|
||||||
|
)
|
||||||
|
|
||||||
|
select
|
||||||
|
ac.*,
|
||||||
|
u.id as user_id,
|
||||||
|
(select cf.id::boolean from community_follower cf where u.id = cf.user_id and ac.id = cf.community_id) as subscribed
|
||||||
|
from user_ u
|
||||||
|
cross join all_community ac
|
||||||
|
|
||||||
|
union all
|
||||||
|
|
||||||
|
select
|
||||||
|
ac.*,
|
||||||
|
null as user_id,
|
||||||
|
null as subscribed
|
||||||
|
from all_community ac
|
||||||
|
;
|
||||||
|
|
||||||
|
-- Post view
|
||||||
|
drop view post_view;
|
||||||
|
create view post_view as
|
||||||
|
with all_post as
|
||||||
|
(
|
||||||
|
select
|
||||||
|
p.*,
|
||||||
|
(select name from user_ where p.creator_id = user_.id) as creator_name,
|
||||||
|
(select name from community where p.community_id = community.id) as community_name,
|
||||||
|
(select removed from community c where p.community_id = c.id) as community_removed,
|
||||||
|
(select deleted from community c where p.community_id = c.id) as community_deleted,
|
||||||
|
(select nsfw from community c where p.community_id = c.id) as community_nsfw,
|
||||||
|
(select count(*) from comment where comment.post_id = p.id) as number_of_comments,
|
||||||
|
coalesce(sum(pl.score), 0) as score,
|
||||||
|
count (case when pl.score = 1 then 1 else null end) as upvotes,
|
||||||
|
count (case when pl.score = -1 then 1 else null end) as downvotes,
|
||||||
|
hot_rank(coalesce(sum(pl.score) , 0), p.published) as hot_rank
|
||||||
|
from post p
|
||||||
|
left join post_like pl on p.id = pl.post_id
|
||||||
|
group by p.id
|
||||||
|
)
|
||||||
|
|
||||||
|
select
|
||||||
|
ap.*,
|
||||||
|
u.id as user_id,
|
||||||
|
coalesce(pl.score, 0) as my_vote,
|
||||||
|
(select cf.id::bool from community_follower cf where u.id = cf.user_id and cf.community_id = ap.community_id) as subscribed,
|
||||||
|
(select pr.id::bool from post_read pr where u.id = pr.user_id and pr.post_id = ap.id) as read,
|
||||||
|
(select ps.id::bool from post_saved ps where u.id = ps.user_id and ps.post_id = ap.id) as saved
|
||||||
|
from user_ u
|
||||||
|
cross join all_post ap
|
||||||
|
left join post_like pl on u.id = pl.user_id and ap.id = pl.post_id
|
||||||
|
|
||||||
|
union all
|
||||||
|
|
||||||
|
select
|
||||||
|
ap.*,
|
||||||
|
null as user_id,
|
||||||
|
null as my_vote,
|
||||||
|
null as subscribed,
|
||||||
|
null as read,
|
||||||
|
null as saved
|
||||||
|
from all_post ap
|
||||||
|
;
|
|
@ -22,7 +22,8 @@ pub struct CreateCommunity {
|
||||||
name: String,
|
name: String,
|
||||||
title: String,
|
title: String,
|
||||||
description: Option<String>,
|
description: Option<String>,
|
||||||
category_id: i32 ,
|
category_id: i32,
|
||||||
|
nsfw: bool,
|
||||||
auth: String
|
auth: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,6 +87,7 @@ pub struct EditCommunity {
|
||||||
category_id: i32,
|
category_id: i32,
|
||||||
removed: Option<bool>,
|
removed: Option<bool>,
|
||||||
deleted: Option<bool>,
|
deleted: Option<bool>,
|
||||||
|
nsfw: bool,
|
||||||
reason: Option<String>,
|
reason: Option<String>,
|
||||||
expires: Option<i64>,
|
expires: Option<i64>,
|
||||||
auth: String
|
auth: String
|
||||||
|
@ -194,6 +196,7 @@ impl Perform<CommunityResponse> for Oper<CreateCommunity> {
|
||||||
creator_id: user_id,
|
creator_id: user_id,
|
||||||
removed: None,
|
removed: None,
|
||||||
deleted: None,
|
deleted: None,
|
||||||
|
nsfw: data.nsfw,
|
||||||
updated: None,
|
updated: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -291,6 +294,7 @@ impl Perform<CommunityResponse> for Oper<EditCommunity> {
|
||||||
creator_id: user_id,
|
creator_id: user_id,
|
||||||
removed: data.removed.to_owned(),
|
removed: data.removed.to_owned(),
|
||||||
deleted: data.deleted.to_owned(),
|
deleted: data.deleted.to_owned(),
|
||||||
|
nsfw: data.nsfw,
|
||||||
updated: Some(naive_now())
|
updated: Some(naive_now())
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -333,12 +337,11 @@ impl Perform<ListCommunitiesResponse> for Oper<ListCommunities> {
|
||||||
let data: &ListCommunities = &self.data;
|
let data: &ListCommunities = &self.data;
|
||||||
let conn = establish_connection();
|
let conn = establish_connection();
|
||||||
|
|
||||||
let user_id: Option<i32> = match &data.auth {
|
let user_claims: Option<Claims> = match &data.auth {
|
||||||
Some(auth) => {
|
Some(auth) => {
|
||||||
match Claims::decode(&auth) {
|
match Claims::decode(&auth) {
|
||||||
Ok(claims) => {
|
Ok(claims) => {
|
||||||
let user_id = claims.claims.id;
|
Some(claims.claims)
|
||||||
Some(user_id)
|
|
||||||
}
|
}
|
||||||
Err(_e) => None
|
Err(_e) => None
|
||||||
}
|
}
|
||||||
|
@ -346,9 +349,26 @@ impl Perform<ListCommunitiesResponse> for Oper<ListCommunities> {
|
||||||
None => None
|
None => None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let user_id = match &user_claims {
|
||||||
|
Some(claims) => Some(claims.id),
|
||||||
|
None => None
|
||||||
|
};
|
||||||
|
|
||||||
|
let show_nsfw = match &user_claims {
|
||||||
|
Some(claims) => claims.show_nsfw,
|
||||||
|
None => false
|
||||||
|
};
|
||||||
|
|
||||||
let sort = SortType::from_str(&data.sort)?;
|
let sort = SortType::from_str(&data.sort)?;
|
||||||
|
|
||||||
let communities: Vec<CommunityView> = CommunityView::list(&conn, &sort, user_id, None, data.page, data.limit)?;
|
let communities: Vec<CommunityView> = CommunityView::list(
|
||||||
|
&conn,
|
||||||
|
&sort,
|
||||||
|
user_id,
|
||||||
|
show_nsfw,
|
||||||
|
None,
|
||||||
|
data.page,
|
||||||
|
data.limit)?;
|
||||||
|
|
||||||
// Return the jwt
|
// Return the jwt
|
||||||
Ok(
|
Ok(
|
||||||
|
|
|
@ -22,7 +22,7 @@ pub mod site;
|
||||||
|
|
||||||
#[derive(EnumString,ToString,Debug)]
|
#[derive(EnumString,ToString,Debug)]
|
||||||
pub enum UserOperation {
|
pub enum UserOperation {
|
||||||
Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, SaveComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, SavePost, EditCommunity, FollowCommunity, GetFollowedCommunities, GetUserDetails, GetReplies, GetModlog, BanFromCommunity, AddModToCommunity, CreateSite, EditSite, GetSite, AddAdmin, BanUser, Search, MarkAllAsRead
|
Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, SaveComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, SavePost, EditCommunity, FollowCommunity, GetFollowedCommunities, GetUserDetails, GetReplies, GetModlog, BanFromCommunity, AddModToCommunity, CreateSite, EditSite, GetSite, AddAdmin, BanUser, Search, MarkAllAsRead, SaveUserSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Fail, Debug)]
|
#[derive(Fail, Debug)]
|
||||||
|
|
|
@ -6,6 +6,7 @@ pub struct CreatePost {
|
||||||
name: String,
|
name: String,
|
||||||
url: Option<String>,
|
url: Option<String>,
|
||||||
body: Option<String>,
|
body: Option<String>,
|
||||||
|
nsfw: bool,
|
||||||
community_id: i32,
|
community_id: i32,
|
||||||
auth: String
|
auth: String
|
||||||
}
|
}
|
||||||
|
@ -73,6 +74,7 @@ pub struct EditPost {
|
||||||
body: Option<String>,
|
body: Option<String>,
|
||||||
removed: Option<bool>,
|
removed: Option<bool>,
|
||||||
deleted: Option<bool>,
|
deleted: Option<bool>,
|
||||||
|
nsfw: bool,
|
||||||
locked: Option<bool>,
|
locked: Option<bool>,
|
||||||
reason: Option<String>,
|
reason: Option<String>,
|
||||||
auth: String
|
auth: String
|
||||||
|
@ -123,6 +125,7 @@ impl Perform<PostResponse> for Oper<CreatePost> {
|
||||||
creator_id: user_id,
|
creator_id: user_id,
|
||||||
removed: None,
|
removed: None,
|
||||||
deleted: None,
|
deleted: None,
|
||||||
|
nsfw: data.nsfw,
|
||||||
locked: None,
|
locked: None,
|
||||||
updated: None
|
updated: None
|
||||||
};
|
};
|
||||||
|
@ -219,12 +222,11 @@ impl Perform<GetPostsResponse> for Oper<GetPosts> {
|
||||||
let data: &GetPosts = &self.data;
|
let data: &GetPosts = &self.data;
|
||||||
let conn = establish_connection();
|
let conn = establish_connection();
|
||||||
|
|
||||||
let user_id: Option<i32> = match &data.auth {
|
let user_claims: Option<Claims> = match &data.auth {
|
||||||
Some(auth) => {
|
Some(auth) => {
|
||||||
match Claims::decode(&auth) {
|
match Claims::decode(&auth) {
|
||||||
Ok(claims) => {
|
Ok(claims) => {
|
||||||
let user_id = claims.claims.id;
|
Some(claims.claims)
|
||||||
Some(user_id)
|
|
||||||
}
|
}
|
||||||
Err(_e) => None
|
Err(_e) => None
|
||||||
}
|
}
|
||||||
|
@ -232,27 +234,38 @@ impl Perform<GetPostsResponse> for Oper<GetPosts> {
|
||||||
None => None
|
None => None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let user_id = match &user_claims {
|
||||||
|
Some(claims) => Some(claims.id),
|
||||||
|
None => None
|
||||||
|
};
|
||||||
|
|
||||||
|
let show_nsfw = match &user_claims {
|
||||||
|
Some(claims) => claims.show_nsfw,
|
||||||
|
None => false
|
||||||
|
};
|
||||||
|
|
||||||
let type_ = PostListingType::from_str(&data.type_)?;
|
let type_ = PostListingType::from_str(&data.type_)?;
|
||||||
let sort = SortType::from_str(&data.sort)?;
|
let sort = SortType::from_str(&data.sort)?;
|
||||||
|
|
||||||
let posts = match PostView::list(&conn,
|
let posts = match PostView::list(
|
||||||
type_,
|
&conn,
|
||||||
&sort,
|
type_,
|
||||||
data.community_id,
|
&sort,
|
||||||
None,
|
data.community_id,
|
||||||
None,
|
None,
|
||||||
user_id,
|
None,
|
||||||
false,
|
user_id,
|
||||||
false,
|
show_nsfw,
|
||||||
data.page,
|
false,
|
||||||
data.limit) {
|
false,
|
||||||
|
data.page,
|
||||||
|
data.limit) {
|
||||||
Ok(posts) => posts,
|
Ok(posts) => posts,
|
||||||
Err(_e) => {
|
Err(_e) => {
|
||||||
return Err(APIError::err(&self.op, "couldnt_get_posts"))?
|
return Err(APIError::err(&self.op, "couldnt_get_posts"))?
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Return the jwt
|
|
||||||
Ok(
|
Ok(
|
||||||
GetPostsResponse {
|
GetPostsResponse {
|
||||||
op: self.op.to_string(),
|
op: self.op.to_string(),
|
||||||
|
@ -381,6 +394,7 @@ impl Perform<PostResponse> for Oper<EditPost> {
|
||||||
community_id: data.community_id,
|
community_id: data.community_id,
|
||||||
removed: data.removed.to_owned(),
|
removed: data.removed.to_owned(),
|
||||||
deleted: data.deleted.to_owned(),
|
deleted: data.deleted.to_owned(),
|
||||||
|
nsfw: data.nsfw,
|
||||||
locked: data.locked.to_owned(),
|
locked: data.locked.to_owned(),
|
||||||
updated: Some(naive_now())
|
updated: Some(naive_now())
|
||||||
};
|
};
|
||||||
|
|
|
@ -277,6 +277,8 @@ impl Perform<SearchResponse> for Oper<Search> {
|
||||||
let mut communities = Vec::new();
|
let mut communities = Vec::new();
|
||||||
let mut users = Vec::new();
|
let mut users = Vec::new();
|
||||||
|
|
||||||
|
// TODO no clean / non-nsfw searching rn
|
||||||
|
|
||||||
match type_ {
|
match type_ {
|
||||||
SearchType::Posts => {
|
SearchType::Posts => {
|
||||||
posts = PostView::list(
|
posts = PostView::list(
|
||||||
|
@ -287,6 +289,7 @@ impl Perform<SearchResponse> for Oper<Search> {
|
||||||
None,
|
None,
|
||||||
Some(data.q.to_owned()),
|
Some(data.q.to_owned()),
|
||||||
None,
|
None,
|
||||||
|
true,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
data.page,
|
data.page,
|
||||||
|
@ -309,6 +312,7 @@ impl Perform<SearchResponse> for Oper<Search> {
|
||||||
&conn,
|
&conn,
|
||||||
&sort,
|
&sort,
|
||||||
None,
|
None,
|
||||||
|
true,
|
||||||
Some(data.q.to_owned()),
|
Some(data.q.to_owned()),
|
||||||
data.page,
|
data.page,
|
||||||
data.limit)?;
|
data.limit)?;
|
||||||
|
@ -330,6 +334,7 @@ impl Perform<SearchResponse> for Oper<Search> {
|
||||||
None,
|
None,
|
||||||
Some(data.q.to_owned()),
|
Some(data.q.to_owned()),
|
||||||
None,
|
None,
|
||||||
|
true,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
data.page,
|
data.page,
|
||||||
|
@ -348,6 +353,7 @@ impl Perform<SearchResponse> for Oper<Search> {
|
||||||
&conn,
|
&conn,
|
||||||
&sort,
|
&sort,
|
||||||
None,
|
None,
|
||||||
|
true,
|
||||||
Some(data.q.to_owned()),
|
Some(data.q.to_owned()),
|
||||||
data.page,
|
data.page,
|
||||||
data.limit)?;
|
data.limit)?;
|
||||||
|
|
|
@ -15,6 +15,13 @@ pub struct Register {
|
||||||
password: String,
|
password: String,
|
||||||
password_verify: String,
|
password_verify: String,
|
||||||
admin: bool,
|
admin: bool,
|
||||||
|
show_nsfw: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct SaveUserSettings {
|
||||||
|
show_nsfw: bool,
|
||||||
|
auth: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
|
@ -151,6 +158,7 @@ impl Perform<LoginResponse> for Oper<Register> {
|
||||||
updated: None,
|
updated: None,
|
||||||
admin: data.admin,
|
admin: data.admin,
|
||||||
banned: false,
|
banned: false,
|
||||||
|
show_nsfw: data.show_nsfw,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create the user
|
// Create the user
|
||||||
|
@ -170,6 +178,7 @@ impl Perform<LoginResponse> for Oper<Register> {
|
||||||
title: "The Default Community".to_string(),
|
title: "The Default Community".to_string(),
|
||||||
description: Some("The Default Community".to_string()),
|
description: Some("The Default Community".to_string()),
|
||||||
category_id: 1,
|
category_id: 1,
|
||||||
|
nsfw: false,
|
||||||
creator_id: inserted_user.id,
|
creator_id: inserted_user.id,
|
||||||
removed: None,
|
removed: None,
|
||||||
deleted: None,
|
deleted: None,
|
||||||
|
@ -218,18 +227,61 @@ impl Perform<LoginResponse> for Oper<Register> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Perform<LoginResponse> for Oper<SaveUserSettings> {
|
||||||
|
fn perform(&self) -> Result<LoginResponse, Error> {
|
||||||
|
let data: &SaveUserSettings = &self.data;
|
||||||
|
let conn = establish_connection();
|
||||||
|
|
||||||
|
let claims = match Claims::decode(&data.auth) {
|
||||||
|
Ok(claims) => claims.claims,
|
||||||
|
Err(_e) => {
|
||||||
|
return Err(APIError::err(&self.op, "not_logged_in"))?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let user_id = claims.id;
|
||||||
|
|
||||||
|
let read_user = User_::read(&conn, user_id)?;
|
||||||
|
|
||||||
|
let user_form = UserForm {
|
||||||
|
name: read_user.name,
|
||||||
|
fedi_name: read_user.fedi_name,
|
||||||
|
email: read_user.email,
|
||||||
|
password_encrypted: read_user.password_encrypted,
|
||||||
|
preferred_username: read_user.preferred_username,
|
||||||
|
updated: Some(naive_now()),
|
||||||
|
admin: read_user.admin,
|
||||||
|
banned: read_user.banned,
|
||||||
|
show_nsfw: data.show_nsfw,
|
||||||
|
};
|
||||||
|
|
||||||
|
let updated_user = match User_::update(&conn, user_id, &user_form) {
|
||||||
|
Ok(user) => user,
|
||||||
|
Err(_e) => {
|
||||||
|
return Err(APIError::err(&self.op, "couldnt_update_user"))?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Return the jwt
|
||||||
|
Ok(
|
||||||
|
LoginResponse {
|
||||||
|
op: self.op.to_string(),
|
||||||
|
jwt: updated_user.jwt()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Perform<GetUserDetailsResponse> for Oper<GetUserDetails> {
|
impl Perform<GetUserDetailsResponse> for Oper<GetUserDetails> {
|
||||||
fn perform(&self) -> Result<GetUserDetailsResponse, Error> {
|
fn perform(&self) -> Result<GetUserDetailsResponse, Error> {
|
||||||
let data: &GetUserDetails = &self.data;
|
let data: &GetUserDetails = &self.data;
|
||||||
let conn = establish_connection();
|
let conn = establish_connection();
|
||||||
|
|
||||||
let user_id: Option<i32> = match &data.auth {
|
let user_claims: Option<Claims> = match &data.auth {
|
||||||
Some(auth) => {
|
Some(auth) => {
|
||||||
match Claims::decode(&auth) {
|
match Claims::decode(&auth) {
|
||||||
Ok(claims) => {
|
Ok(claims) => {
|
||||||
let user_id = claims.claims.id;
|
Some(claims.claims)
|
||||||
Some(user_id)
|
|
||||||
}
|
}
|
||||||
Err(_e) => None
|
Err(_e) => None
|
||||||
}
|
}
|
||||||
|
@ -237,6 +289,16 @@ impl Perform<GetUserDetailsResponse> for Oper<GetUserDetails> {
|
||||||
None => None
|
None => None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let user_id = match &user_claims {
|
||||||
|
Some(claims) => Some(claims.id),
|
||||||
|
None => None
|
||||||
|
};
|
||||||
|
|
||||||
|
let show_nsfw = match &user_claims {
|
||||||
|
Some(claims) => claims.show_nsfw,
|
||||||
|
None => false
|
||||||
|
};
|
||||||
|
|
||||||
//TODO add save
|
//TODO add save
|
||||||
let sort = SortType::from_str(&data.sort)?;
|
let sort = SortType::from_str(&data.sort)?;
|
||||||
|
|
||||||
|
@ -249,50 +311,56 @@ impl Perform<GetUserDetailsResponse> for Oper<GetUserDetails> {
|
||||||
|
|
||||||
// If its saved only, you don't care what creator it was
|
// If its saved only, you don't care what creator it was
|
||||||
let posts = if data.saved_only {
|
let posts = if data.saved_only {
|
||||||
PostView::list(&conn,
|
PostView::list(
|
||||||
PostListingType::All,
|
&conn,
|
||||||
&sort,
|
PostListingType::All,
|
||||||
data.community_id,
|
&sort,
|
||||||
None,
|
data.community_id,
|
||||||
None,
|
None,
|
||||||
Some(user_details_id),
|
None,
|
||||||
data.saved_only,
|
Some(user_details_id),
|
||||||
false,
|
show_nsfw,
|
||||||
data.page,
|
data.saved_only,
|
||||||
data.limit)?
|
false,
|
||||||
|
data.page,
|
||||||
|
data.limit)?
|
||||||
} else {
|
} else {
|
||||||
PostView::list(&conn,
|
PostView::list(
|
||||||
PostListingType::All,
|
&conn,
|
||||||
&sort,
|
PostListingType::All,
|
||||||
data.community_id,
|
&sort,
|
||||||
Some(user_details_id),
|
data.community_id,
|
||||||
None,
|
Some(user_details_id),
|
||||||
user_id,
|
None,
|
||||||
data.saved_only,
|
user_id,
|
||||||
false,
|
show_nsfw,
|
||||||
data.page,
|
data.saved_only,
|
||||||
data.limit)?
|
false,
|
||||||
|
data.page,
|
||||||
|
data.limit)?
|
||||||
};
|
};
|
||||||
let comments = if data.saved_only {
|
let comments = if data.saved_only {
|
||||||
CommentView::list(&conn,
|
CommentView::list(
|
||||||
&sort,
|
&conn,
|
||||||
None,
|
&sort,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
Some(user_details_id),
|
None,
|
||||||
data.saved_only,
|
Some(user_details_id),
|
||||||
data.page,
|
data.saved_only,
|
||||||
data.limit)?
|
data.page,
|
||||||
|
data.limit)?
|
||||||
} else {
|
} else {
|
||||||
CommentView::list(&conn,
|
CommentView::list(
|
||||||
&sort,
|
&conn,
|
||||||
None,
|
&sort,
|
||||||
Some(user_details_id),
|
None,
|
||||||
None,
|
Some(user_details_id),
|
||||||
user_id,
|
None,
|
||||||
data.saved_only,
|
user_id,
|
||||||
data.page,
|
data.saved_only,
|
||||||
data.limit)?
|
data.page,
|
||||||
|
data.limit)?
|
||||||
};
|
};
|
||||||
|
|
||||||
let follows = CommunityFollowerView::for_user(&conn, user_details_id)?;
|
let follows = CommunityFollowerView::for_user(&conn, user_details_id)?;
|
||||||
|
@ -343,6 +411,7 @@ impl Perform<AddAdminResponse> for Oper<AddAdmin> {
|
||||||
updated: Some(naive_now()),
|
updated: Some(naive_now()),
|
||||||
admin: data.added,
|
admin: data.added,
|
||||||
banned: read_user.banned,
|
banned: read_user.banned,
|
||||||
|
show_nsfw: read_user.show_nsfw,
|
||||||
};
|
};
|
||||||
|
|
||||||
match User_::update(&conn, data.user_id, &user_form) {
|
match User_::update(&conn, data.user_id, &user_form) {
|
||||||
|
@ -402,6 +471,7 @@ impl Perform<BanUserResponse> for Oper<BanUser> {
|
||||||
updated: Some(naive_now()),
|
updated: Some(naive_now()),
|
||||||
admin: read_user.admin,
|
admin: read_user.admin,
|
||||||
banned: data.ban,
|
banned: data.ban,
|
||||||
|
show_nsfw: read_user.show_nsfw,
|
||||||
};
|
};
|
||||||
|
|
||||||
match User_::update(&conn, data.user_id, &user_form) {
|
match User_::update(&conn, data.user_id, &user_form) {
|
||||||
|
|
|
@ -46,7 +46,8 @@ mod tests {
|
||||||
published: naive_now(),
|
published: naive_now(),
|
||||||
admin: false,
|
admin: false,
|
||||||
banned: false,
|
banned: false,
|
||||||
updated: None
|
updated: None,
|
||||||
|
show_nsfw: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let person = expected_user.person();
|
let person = expected_user.person();
|
||||||
|
|
|
@ -170,7 +170,8 @@ mod tests {
|
||||||
email: None,
|
email: None,
|
||||||
admin: false,
|
admin: false,
|
||||||
banned: false,
|
banned: false,
|
||||||
updated: None
|
updated: None,
|
||||||
|
show_nsfw: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
||||||
|
@ -183,7 +184,8 @@ mod tests {
|
||||||
creator_id: inserted_user.id,
|
creator_id: inserted_user.id,
|
||||||
removed: None,
|
removed: None,
|
||||||
deleted: None,
|
deleted: None,
|
||||||
updated: None
|
updated: None,
|
||||||
|
nsfw: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_community = Community::create(&conn, &new_community).unwrap();
|
let inserted_community = Community::create(&conn, &new_community).unwrap();
|
||||||
|
@ -197,7 +199,8 @@ mod tests {
|
||||||
removed: None,
|
removed: None,
|
||||||
deleted: None,
|
deleted: None,
|
||||||
locked: None,
|
locked: None,
|
||||||
updated: None
|
updated: None,
|
||||||
|
nsfw: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_post = Post::create(&conn, &new_post).unwrap();
|
let inserted_post = Post::create(&conn, &new_post).unwrap();
|
||||||
|
|
|
@ -261,7 +261,8 @@ mod tests {
|
||||||
email: None,
|
email: None,
|
||||||
admin: false,
|
admin: false,
|
||||||
banned: false,
|
banned: false,
|
||||||
updated: None
|
updated: None,
|
||||||
|
show_nsfw: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
||||||
|
@ -274,7 +275,8 @@ mod tests {
|
||||||
creator_id: inserted_user.id,
|
creator_id: inserted_user.id,
|
||||||
removed: None,
|
removed: None,
|
||||||
deleted: None,
|
deleted: None,
|
||||||
updated: None
|
updated: None,
|
||||||
|
nsfw: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_community = Community::create(&conn, &new_community).unwrap();
|
let inserted_community = Community::create(&conn, &new_community).unwrap();
|
||||||
|
@ -288,7 +290,8 @@ mod tests {
|
||||||
removed: None,
|
removed: None,
|
||||||
deleted: None,
|
deleted: None,
|
||||||
locked: None,
|
locked: None,
|
||||||
updated: None
|
updated: None,
|
||||||
|
nsfw: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_post = Post::create(&conn, &new_post).unwrap();
|
let inserted_post = Post::create(&conn, &new_post).unwrap();
|
||||||
|
|
|
@ -14,6 +14,7 @@ pub struct Community {
|
||||||
pub published: chrono::NaiveDateTime,
|
pub published: chrono::NaiveDateTime,
|
||||||
pub updated: Option<chrono::NaiveDateTime>,
|
pub updated: Option<chrono::NaiveDateTime>,
|
||||||
pub deleted: bool,
|
pub deleted: bool,
|
||||||
|
pub nsfw: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Insertable, AsChangeset, Clone, Serialize, Deserialize)]
|
#[derive(Insertable, AsChangeset, Clone, Serialize, Deserialize)]
|
||||||
|
@ -27,6 +28,7 @@ pub struct CommunityForm {
|
||||||
pub removed: Option<bool>,
|
pub removed: Option<bool>,
|
||||||
pub updated: Option<chrono::NaiveDateTime>,
|
pub updated: Option<chrono::NaiveDateTime>,
|
||||||
pub deleted: Option<bool>,
|
pub deleted: Option<bool>,
|
||||||
|
pub nsfw: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Crud<CommunityForm> for Community {
|
impl Crud<CommunityForm> for Community {
|
||||||
|
@ -229,7 +231,8 @@ mod tests {
|
||||||
email: None,
|
email: None,
|
||||||
admin: false,
|
admin: false,
|
||||||
banned: false,
|
banned: false,
|
||||||
updated: None
|
updated: None,
|
||||||
|
show_nsfw: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
||||||
|
@ -240,6 +243,7 @@ mod tests {
|
||||||
title: "nada".to_owned(),
|
title: "nada".to_owned(),
|
||||||
description: None,
|
description: None,
|
||||||
category_id: 1,
|
category_id: 1,
|
||||||
|
nsfw: false,
|
||||||
removed: None,
|
removed: None,
|
||||||
deleted: None,
|
deleted: None,
|
||||||
updated: None,
|
updated: None,
|
||||||
|
@ -254,6 +258,7 @@ mod tests {
|
||||||
title: "nada".to_owned(),
|
title: "nada".to_owned(),
|
||||||
description: None,
|
description: None,
|
||||||
category_id: 1,
|
category_id: 1,
|
||||||
|
nsfw: false,
|
||||||
removed: false,
|
removed: false,
|
||||||
deleted: false,
|
deleted: false,
|
||||||
published: inserted_community.published,
|
published: inserted_community.published,
|
||||||
|
|
|
@ -12,6 +12,7 @@ table! {
|
||||||
published -> Timestamp,
|
published -> Timestamp,
|
||||||
updated -> Nullable<Timestamp>,
|
updated -> Nullable<Timestamp>,
|
||||||
deleted -> Bool,
|
deleted -> Bool,
|
||||||
|
nsfw -> Bool,
|
||||||
creator_name -> Varchar,
|
creator_name -> Varchar,
|
||||||
category_name -> Varchar,
|
category_name -> Varchar,
|
||||||
number_of_subscribers -> BigInt,
|
number_of_subscribers -> BigInt,
|
||||||
|
@ -84,6 +85,7 @@ pub struct CommunityView {
|
||||||
pub published: chrono::NaiveDateTime,
|
pub published: chrono::NaiveDateTime,
|
||||||
pub updated: Option<chrono::NaiveDateTime>,
|
pub updated: Option<chrono::NaiveDateTime>,
|
||||||
pub deleted: bool,
|
pub deleted: bool,
|
||||||
|
pub nsfw: bool,
|
||||||
pub creator_name: String,
|
pub creator_name: String,
|
||||||
pub category_name: String,
|
pub category_name: String,
|
||||||
pub number_of_subscribers: i64,
|
pub number_of_subscribers: i64,
|
||||||
|
@ -112,13 +114,15 @@ impl CommunityView {
|
||||||
query.first::<Self>(conn)
|
query.first::<Self>(conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list(conn: &PgConnection,
|
pub fn list(
|
||||||
sort: &SortType,
|
conn: &PgConnection,
|
||||||
from_user_id: Option<i32>,
|
sort: &SortType,
|
||||||
search_term: Option<String>,
|
from_user_id: Option<i32>,
|
||||||
page: Option<i64>,
|
show_nsfw: bool,
|
||||||
limit: Option<i64>,
|
search_term: Option<String>,
|
||||||
) -> Result<Vec<Self>, Error> {
|
page: Option<i64>,
|
||||||
|
limit: Option<i64>,
|
||||||
|
) -> Result<Vec<Self>, Error> {
|
||||||
use super::community_view::community_view::dsl::*;
|
use super::community_view::community_view::dsl::*;
|
||||||
let mut query = community_view.into_boxed();
|
let mut query = community_view.into_boxed();
|
||||||
|
|
||||||
|
@ -143,6 +147,10 @@ impl CommunityView {
|
||||||
_ => ()
|
_ => ()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if !show_nsfw {
|
||||||
|
query = query.filter(nsfw.eq(false));
|
||||||
|
};
|
||||||
|
|
||||||
query
|
query
|
||||||
.limit(limit)
|
.limit(limit)
|
||||||
.offset(offset)
|
.offset(offset)
|
||||||
|
|
|
@ -412,7 +412,8 @@ mod tests {
|
||||||
email: None,
|
email: None,
|
||||||
admin: false,
|
admin: false,
|
||||||
banned: false,
|
banned: false,
|
||||||
updated: None
|
updated: None,
|
||||||
|
show_nsfw: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_mod = User_::create(&conn, &new_mod).unwrap();
|
let inserted_mod = User_::create(&conn, &new_mod).unwrap();
|
||||||
|
@ -425,7 +426,8 @@ mod tests {
|
||||||
email: None,
|
email: None,
|
||||||
admin: false,
|
admin: false,
|
||||||
banned: false,
|
banned: false,
|
||||||
updated: None
|
updated: None,
|
||||||
|
show_nsfw: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
||||||
|
@ -438,7 +440,8 @@ mod tests {
|
||||||
creator_id: inserted_user.id,
|
creator_id: inserted_user.id,
|
||||||
removed: None,
|
removed: None,
|
||||||
deleted: None,
|
deleted: None,
|
||||||
updated: None
|
updated: None,
|
||||||
|
nsfw: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_community = Community::create(&conn, &new_community).unwrap();
|
let inserted_community = Community::create(&conn, &new_community).unwrap();
|
||||||
|
@ -452,7 +455,8 @@ mod tests {
|
||||||
removed: None,
|
removed: None,
|
||||||
deleted: None,
|
deleted: None,
|
||||||
locked: None,
|
locked: None,
|
||||||
updated: None
|
updated: None,
|
||||||
|
nsfw: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_post = Post::create(&conn, &new_post).unwrap();
|
let inserted_post = Post::create(&conn, &new_post).unwrap();
|
||||||
|
|
|
@ -15,6 +15,7 @@ pub struct Post {
|
||||||
pub published: chrono::NaiveDateTime,
|
pub published: chrono::NaiveDateTime,
|
||||||
pub updated: Option<chrono::NaiveDateTime>,
|
pub updated: Option<chrono::NaiveDateTime>,
|
||||||
pub deleted: bool,
|
pub deleted: bool,
|
||||||
|
pub nsfw: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Insertable, AsChangeset, Clone)]
|
#[derive(Insertable, AsChangeset, Clone)]
|
||||||
|
@ -29,6 +30,7 @@ pub struct PostForm {
|
||||||
pub locked: Option<bool>,
|
pub locked: Option<bool>,
|
||||||
pub updated: Option<chrono::NaiveDateTime>,
|
pub updated: Option<chrono::NaiveDateTime>,
|
||||||
pub deleted: Option<bool>,
|
pub deleted: Option<bool>,
|
||||||
|
pub nsfw: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Crud<PostForm> for Post {
|
impl Crud<PostForm> for Post {
|
||||||
|
@ -183,7 +185,8 @@ mod tests {
|
||||||
email: None,
|
email: None,
|
||||||
admin: false,
|
admin: false,
|
||||||
banned: false,
|
banned: false,
|
||||||
updated: None
|
updated: None,
|
||||||
|
show_nsfw: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
||||||
|
@ -196,7 +199,8 @@ mod tests {
|
||||||
creator_id: inserted_user.id,
|
creator_id: inserted_user.id,
|
||||||
removed: None,
|
removed: None,
|
||||||
deleted: None,
|
deleted: None,
|
||||||
updated: None
|
updated: None,
|
||||||
|
nsfw: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_community = Community::create(&conn, &new_community).unwrap();
|
let inserted_community = Community::create(&conn, &new_community).unwrap();
|
||||||
|
@ -210,6 +214,7 @@ mod tests {
|
||||||
removed: None,
|
removed: None,
|
||||||
deleted: None,
|
deleted: None,
|
||||||
locked: None,
|
locked: None,
|
||||||
|
nsfw: false,
|
||||||
updated: None
|
updated: None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -225,6 +230,7 @@ mod tests {
|
||||||
published: inserted_post.published,
|
published: inserted_post.published,
|
||||||
removed: false,
|
removed: false,
|
||||||
locked: false,
|
locked: false,
|
||||||
|
nsfw: false,
|
||||||
deleted: false,
|
deleted: false,
|
||||||
updated: None
|
updated: None
|
||||||
};
|
};
|
||||||
|
|
|
@ -19,10 +19,12 @@ table! {
|
||||||
published -> Timestamp,
|
published -> Timestamp,
|
||||||
updated -> Nullable<Timestamp>,
|
updated -> Nullable<Timestamp>,
|
||||||
deleted -> Bool,
|
deleted -> Bool,
|
||||||
|
nsfw -> Bool,
|
||||||
creator_name -> Varchar,
|
creator_name -> Varchar,
|
||||||
community_name -> Varchar,
|
community_name -> Varchar,
|
||||||
community_removed -> Bool,
|
community_removed -> Bool,
|
||||||
community_deleted -> Bool,
|
community_deleted -> Bool,
|
||||||
|
community_nsfw -> Bool,
|
||||||
number_of_comments -> BigInt,
|
number_of_comments -> BigInt,
|
||||||
score -> BigInt,
|
score -> BigInt,
|
||||||
upvotes -> BigInt,
|
upvotes -> BigInt,
|
||||||
|
@ -51,10 +53,12 @@ pub struct PostView {
|
||||||
pub published: chrono::NaiveDateTime,
|
pub published: chrono::NaiveDateTime,
|
||||||
pub updated: Option<chrono::NaiveDateTime>,
|
pub updated: Option<chrono::NaiveDateTime>,
|
||||||
pub deleted: bool,
|
pub deleted: bool,
|
||||||
|
pub nsfw: bool,
|
||||||
pub creator_name: String,
|
pub creator_name: String,
|
||||||
pub community_name: String,
|
pub community_name: String,
|
||||||
pub community_removed: bool,
|
pub community_removed: bool,
|
||||||
pub community_deleted: bool,
|
pub community_deleted: bool,
|
||||||
|
pub community_nsfw: bool,
|
||||||
pub number_of_comments: i64,
|
pub number_of_comments: i64,
|
||||||
pub score: i64,
|
pub score: i64,
|
||||||
pub upvotes: i64,
|
pub upvotes: i64,
|
||||||
|
@ -68,18 +72,20 @@ pub struct PostView {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PostView {
|
impl PostView {
|
||||||
pub fn list(conn: &PgConnection,
|
pub fn list(
|
||||||
type_: PostListingType,
|
conn: &PgConnection,
|
||||||
sort: &SortType,
|
type_: PostListingType,
|
||||||
for_community_id: Option<i32>,
|
sort: &SortType,
|
||||||
for_creator_id: Option<i32>,
|
for_community_id: Option<i32>,
|
||||||
search_term: Option<String>,
|
for_creator_id: Option<i32>,
|
||||||
my_user_id: Option<i32>,
|
search_term: Option<String>,
|
||||||
saved_only: bool,
|
my_user_id: Option<i32>,
|
||||||
unread_only: bool,
|
show_nsfw: bool,
|
||||||
page: Option<i64>,
|
saved_only: bool,
|
||||||
limit: Option<i64>,
|
unread_only: bool,
|
||||||
) -> Result<Vec<Self>, Error> {
|
page: Option<i64>,
|
||||||
|
limit: Option<i64>,
|
||||||
|
) -> Result<Vec<Self>, Error> {
|
||||||
use super::post_view::post_view::dsl::*;
|
use super::post_view::post_view::dsl::*;
|
||||||
|
|
||||||
let (limit, offset) = limit_and_offset(page, limit);
|
let (limit, offset) = limit_and_offset(page, limit);
|
||||||
|
@ -121,6 +127,12 @@ impl PostView {
|
||||||
query = query.filter(user_id.is_null());
|
query = query.filter(user_id.is_null());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !show_nsfw {
|
||||||
|
query = query
|
||||||
|
.filter(nsfw.eq(false))
|
||||||
|
.filter(community_nsfw.eq(false));
|
||||||
|
};
|
||||||
|
|
||||||
query = match sort {
|
query = match sort {
|
||||||
SortType::Hot => query.order_by(hot_rank.desc())
|
SortType::Hot => query.order_by(hot_rank.desc())
|
||||||
.then_order_by(published.desc()),
|
.then_order_by(published.desc()),
|
||||||
|
@ -196,6 +208,7 @@ mod tests {
|
||||||
updated: None,
|
updated: None,
|
||||||
admin: false,
|
admin: false,
|
||||||
banned: false,
|
banned: false,
|
||||||
|
show_nsfw: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
||||||
|
@ -208,7 +221,8 @@ mod tests {
|
||||||
category_id: 1,
|
category_id: 1,
|
||||||
removed: None,
|
removed: None,
|
||||||
deleted: None,
|
deleted: None,
|
||||||
updated: None
|
updated: None,
|
||||||
|
nsfw: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_community = Community::create(&conn, &new_community).unwrap();
|
let inserted_community = Community::create(&conn, &new_community).unwrap();
|
||||||
|
@ -222,7 +236,8 @@ mod tests {
|
||||||
removed: None,
|
removed: None,
|
||||||
deleted: None,
|
deleted: None,
|
||||||
locked: None,
|
locked: None,
|
||||||
updated: None
|
updated: None,
|
||||||
|
nsfw: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_post = Post::create(&conn, &new_post).unwrap();
|
let inserted_post = Post::create(&conn, &new_post).unwrap();
|
||||||
|
@ -266,6 +281,7 @@ mod tests {
|
||||||
community_name: community_name.to_owned(),
|
community_name: community_name.to_owned(),
|
||||||
community_removed: false,
|
community_removed: false,
|
||||||
community_deleted: false,
|
community_deleted: false,
|
||||||
|
community_nsfw: false,
|
||||||
number_of_comments: 0,
|
number_of_comments: 0,
|
||||||
score: 1,
|
score: 1,
|
||||||
upvotes: 1,
|
upvotes: 1,
|
||||||
|
@ -276,6 +292,7 @@ mod tests {
|
||||||
subscribed: None,
|
subscribed: None,
|
||||||
read: None,
|
read: None,
|
||||||
saved: None,
|
saved: None,
|
||||||
|
nsfw: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let expected_post_listing_with_user = PostView {
|
let expected_post_listing_with_user = PostView {
|
||||||
|
@ -294,6 +311,7 @@ mod tests {
|
||||||
community_name: community_name.to_owned(),
|
community_name: community_name.to_owned(),
|
||||||
community_removed: false,
|
community_removed: false,
|
||||||
community_deleted: false,
|
community_deleted: false,
|
||||||
|
community_nsfw: false,
|
||||||
number_of_comments: 0,
|
number_of_comments: 0,
|
||||||
score: 1,
|
score: 1,
|
||||||
upvotes: 1,
|
upvotes: 1,
|
||||||
|
@ -304,6 +322,7 @@ mod tests {
|
||||||
subscribed: None,
|
subscribed: None,
|
||||||
read: None,
|
read: None,
|
||||||
saved: None,
|
saved: None,
|
||||||
|
nsfw: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -315,6 +334,7 @@ mod tests {
|
||||||
Some(inserted_user.id),
|
Some(inserted_user.id),
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
None,
|
None,
|
||||||
None).unwrap();
|
None).unwrap();
|
||||||
let read_post_listings_no_user = PostView::list(&conn,
|
let read_post_listings_no_user = PostView::list(&conn,
|
||||||
|
@ -326,6 +346,7 @@ mod tests {
|
||||||
None,
|
None,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
None,
|
None,
|
||||||
None).unwrap();
|
None).unwrap();
|
||||||
let read_post_listing_no_user = PostView::read(&conn, inserted_post.id, None).unwrap();
|
let read_post_listing_no_user = PostView::read(&conn, inserted_post.id, None).unwrap();
|
||||||
|
|
|
@ -18,7 +18,8 @@ pub struct User_ {
|
||||||
pub admin: bool,
|
pub admin: bool,
|
||||||
pub banned: bool,
|
pub banned: bool,
|
||||||
pub published: chrono::NaiveDateTime,
|
pub published: chrono::NaiveDateTime,
|
||||||
pub updated: Option<chrono::NaiveDateTime>
|
pub updated: Option<chrono::NaiveDateTime>,
|
||||||
|
pub show_nsfw: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Insertable, AsChangeset, Clone)]
|
#[derive(Insertable, AsChangeset, Clone)]
|
||||||
|
@ -31,7 +32,8 @@ pub struct UserForm {
|
||||||
pub admin: bool,
|
pub admin: bool,
|
||||||
pub banned: bool,
|
pub banned: bool,
|
||||||
pub email: Option<String>,
|
pub email: Option<String>,
|
||||||
pub updated: Option<chrono::NaiveDateTime>
|
pub updated: Option<chrono::NaiveDateTime>,
|
||||||
|
pub show_nsfw: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Crud<UserForm> for User_ {
|
impl Crud<UserForm> for User_ {
|
||||||
|
@ -77,6 +79,7 @@ pub struct Claims {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub username: String,
|
pub username: String,
|
||||||
pub iss: String,
|
pub iss: String,
|
||||||
|
pub show_nsfw: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Claims {
|
impl Claims {
|
||||||
|
@ -96,6 +99,7 @@ impl User_ {
|
||||||
id: self.id,
|
id: self.id,
|
||||||
username: self.name.to_owned(),
|
username: self.name.to_owned(),
|
||||||
iss: self.fedi_name.to_owned(),
|
iss: self.fedi_name.to_owned(),
|
||||||
|
show_nsfw: self.show_nsfw,
|
||||||
};
|
};
|
||||||
encode(&Header::default(), &my_claims, Settings::get().jwt_secret.as_ref()).unwrap()
|
encode(&Header::default(), &my_claims, Settings::get().jwt_secret.as_ref()).unwrap()
|
||||||
}
|
}
|
||||||
|
@ -133,7 +137,8 @@ mod tests {
|
||||||
email: None,
|
email: None,
|
||||||
admin: false,
|
admin: false,
|
||||||
banned: false,
|
banned: false,
|
||||||
updated: None
|
updated: None,
|
||||||
|
show_nsfw: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
||||||
|
@ -149,7 +154,8 @@ mod tests {
|
||||||
admin: false,
|
admin: false,
|
||||||
banned: false,
|
banned: false,
|
||||||
published: inserted_user.published,
|
published: inserted_user.published,
|
||||||
updated: None
|
updated: None,
|
||||||
|
show_nsfw: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let read_user = User_::read(&conn, inserted_user.id).unwrap();
|
let read_user = User_::read(&conn, inserted_user.id).unwrap();
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#![recursion_limit = "512"]
|
||||||
#[macro_use] pub extern crate strum_macros;
|
#[macro_use] pub extern crate strum_macros;
|
||||||
#[macro_use] pub extern crate lazy_static;
|
#[macro_use] pub extern crate lazy_static;
|
||||||
#[macro_use] pub extern crate failure;
|
#[macro_use] pub extern crate failure;
|
||||||
|
|
|
@ -52,6 +52,7 @@ table! {
|
||||||
published -> Timestamp,
|
published -> Timestamp,
|
||||||
updated -> Nullable<Timestamp>,
|
updated -> Nullable<Timestamp>,
|
||||||
deleted -> Bool,
|
deleted -> Bool,
|
||||||
|
nsfw -> Bool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,6 +186,7 @@ table! {
|
||||||
published -> Timestamp,
|
published -> Timestamp,
|
||||||
updated -> Nullable<Timestamp>,
|
updated -> Nullable<Timestamp>,
|
||||||
deleted -> Bool,
|
deleted -> Bool,
|
||||||
|
nsfw -> Bool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,6 +242,7 @@ table! {
|
||||||
banned -> Bool,
|
banned -> Bool,
|
||||||
published -> Timestamp,
|
published -> Timestamp,
|
||||||
updated -> Nullable<Timestamp>,
|
updated -> Nullable<Timestamp>,
|
||||||
|
show_nsfw -> Bool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -134,17 +134,19 @@ impl ChatServer {
|
||||||
use crate::db::*;
|
use crate::db::*;
|
||||||
use crate::db::post_view::*;
|
use crate::db::post_view::*;
|
||||||
let conn = establish_connection();
|
let conn = establish_connection();
|
||||||
let posts = PostView::list(&conn,
|
let posts = PostView::list(
|
||||||
PostListingType::Community,
|
&conn,
|
||||||
&SortType::New,
|
PostListingType::Community,
|
||||||
Some(*community_id),
|
&SortType::New,
|
||||||
None,
|
Some(*community_id),
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
false,
|
None,
|
||||||
false,
|
false,
|
||||||
None,
|
false,
|
||||||
Some(9999))?;
|
false,
|
||||||
|
None,
|
||||||
|
Some(9999))?;
|
||||||
for post in posts {
|
for post in posts {
|
||||||
self.send_room_message(&post.id, message, skip_id);
|
self.send_room_message(&post.id, message, skip_id);
|
||||||
}
|
}
|
||||||
|
@ -303,6 +305,11 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
|
||||||
let res = Oper::new(user_operation, get_user_details).perform()?;
|
let res = Oper::new(user_operation, get_user_details).perform()?;
|
||||||
Ok(serde_json::to_string(&res)?)
|
Ok(serde_json::to_string(&res)?)
|
||||||
},
|
},
|
||||||
|
UserOperation::SaveUserSettings => {
|
||||||
|
let save_user_settings: SaveUserSettings = serde_json::from_str(data)?;
|
||||||
|
let res = Oper::new(user_operation, save_user_settings).perform()?;
|
||||||
|
Ok(serde_json::to_string(&res)?)
|
||||||
|
},
|
||||||
UserOperation::AddAdmin => {
|
UserOperation::AddAdmin => {
|
||||||
let add_admin: AddAdmin = serde_json::from_str(data)?;
|
let add_admin: AddAdmin = serde_json::from_str(data)?;
|
||||||
let res = Oper::new(user_operation, add_admin).perform()?;
|
let res = Oper::new(user_operation, add_admin).perform()?;
|
||||||
|
|
|
@ -41,6 +41,6 @@
|
||||||
"fuse-box": "^3.1.3",
|
"fuse-box": "^3.1.3",
|
||||||
"ts-transform-classcat": "^0.0.2",
|
"ts-transform-classcat": "^0.0.2",
|
||||||
"ts-transform-inferno": "^4.0.2",
|
"ts-transform-inferno": "^4.0.2",
|
||||||
"typescript": "^3.3.3333"
|
"typescript": "^3.5.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,8 @@ export class CommunityForm extends Component<CommunityFormProps, CommunityFormSt
|
||||||
communityForm: {
|
communityForm: {
|
||||||
name: null,
|
name: null,
|
||||||
title: null,
|
title: null,
|
||||||
category_id: null
|
category_id: null,
|
||||||
|
nsfw: false,
|
||||||
},
|
},
|
||||||
categories: [],
|
categories: [],
|
||||||
loading: false
|
loading: false
|
||||||
|
@ -48,6 +49,7 @@ export class CommunityForm extends Component<CommunityFormProps, CommunityFormSt
|
||||||
category_id: this.props.community.category_id,
|
category_id: this.props.community.category_id,
|
||||||
description: this.props.community.description,
|
description: this.props.community.description,
|
||||||
edit_id: this.props.community.id,
|
edit_id: this.props.community.id,
|
||||||
|
nsfw: this.props.community.nsfw,
|
||||||
auth: null
|
auth: null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,6 +105,14 @@ export class CommunityForm extends Component<CommunityFormProps, CommunityFormSt
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group row">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" checked={this.state.communityForm.nsfw} onChange={linkEvent(this, this.handleCommunityNsfwChange)}/>
|
||||||
|
<label class="form-check-label"><T i18nKey="nsfw">#</T></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<button type="submit" class="btn btn-secondary mr-2">
|
<button type="submit" class="btn btn-secondary mr-2">
|
||||||
|
@ -147,6 +157,11 @@ export class CommunityForm extends Component<CommunityFormProps, CommunityFormSt
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleCommunityNsfwChange(i: CommunityForm, event: any) {
|
||||||
|
i.state.communityForm.nsfw = event.target.checked;
|
||||||
|
i.setState(i.state);
|
||||||
|
}
|
||||||
|
|
||||||
handleCancel(i: CommunityForm) {
|
handleCancel(i: CommunityForm) {
|
||||||
i.props.onCancel();
|
i.props.onCancel();
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ export class Community extends Component<any, State> {
|
||||||
number_of_comments: null,
|
number_of_comments: null,
|
||||||
published: null,
|
published: null,
|
||||||
removed: null,
|
removed: null,
|
||||||
|
nsfw: false,
|
||||||
deleted: null,
|
deleted: null,
|
||||||
},
|
},
|
||||||
moderators: [],
|
moderators: [],
|
||||||
|
@ -105,6 +106,9 @@ export class Community extends Component<any, State> {
|
||||||
{this.state.community.removed &&
|
{this.state.community.removed &&
|
||||||
<small className="ml-2 text-muted font-italic"><T i18nKey="removed">#</T></small>
|
<small className="ml-2 text-muted font-italic"><T i18nKey="removed">#</T></small>
|
||||||
}
|
}
|
||||||
|
{this.state.community.nsfw &&
|
||||||
|
<small className="ml-2 text-muted font-italic"><T i18nKey="nsfw">#</T></small>
|
||||||
|
}
|
||||||
</h5>
|
</h5>
|
||||||
{this.selects()}
|
{this.selects()}
|
||||||
<PostListings posts={this.state.posts} />
|
<PostListings posts={this.state.posts} />
|
||||||
|
|
|
@ -28,6 +28,7 @@ export class Login extends Component<any, State> {
|
||||||
password: undefined,
|
password: undefined,
|
||||||
password_verify: undefined,
|
password_verify: undefined,
|
||||||
admin: false,
|
admin: false,
|
||||||
|
show_nsfw: false,
|
||||||
},
|
},
|
||||||
loginLoading: false,
|
loginLoading: false,
|
||||||
registerLoading: false,
|
registerLoading: false,
|
||||||
|
@ -125,11 +126,18 @@ export class Login extends Component<any, State> {
|
||||||
<input type="password" value={this.state.registerForm.password_verify} onInput={linkEvent(this, this.handleRegisterPasswordVerifyChange)} class="form-control" required />
|
<input type="password" value={this.state.registerForm.password_verify} onInput={linkEvent(this, this.handleRegisterPasswordVerifyChange)} class="form-control" required />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group row">
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" checked={this.state.registerForm.show_nsfw} onChange={linkEvent(this, this.handleRegisterShowNsfwChange)}/>
|
||||||
|
<label class="form-check-label"><T i18nKey="show_nsfw">#</T></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<button type="submit" class="btn btn-secondary">{this.state.registerLoading ?
|
<button type="submit" class="btn btn-secondary">{this.state.registerLoading ?
|
||||||
<svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg> : i18n.t('sign_up')}</button>
|
<svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg> : i18n.t('sign_up')}</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -181,6 +189,11 @@ export class Login extends Component<any, State> {
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleRegisterShowNsfwChange(i: Login, event: any) {
|
||||||
|
i.state.registerForm.show_nsfw = event.target.checked;
|
||||||
|
i.setState(i.state);
|
||||||
|
}
|
||||||
|
|
||||||
parseMessage(msg: any) {
|
parseMessage(msg: any) {
|
||||||
let op: UserOperation = msgOp(msg);
|
let op: UserOperation = msgOp(msg);
|
||||||
if (msg.error) {
|
if (msg.error) {
|
||||||
|
|
|
@ -31,6 +31,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
private emptyState: PostFormState = {
|
private emptyState: PostFormState = {
|
||||||
postForm: {
|
postForm: {
|
||||||
name: null,
|
name: null,
|
||||||
|
nsfw: false,
|
||||||
auth: null,
|
auth: null,
|
||||||
community_id: null,
|
community_id: null,
|
||||||
creator_id: (UserService.Instance.user) ? UserService.Instance.user.id : null,
|
creator_id: (UserService.Instance.user) ? UserService.Instance.user.id : null,
|
||||||
|
@ -54,6 +55,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
edit_id: this.props.post.id,
|
edit_id: this.props.post.id,
|
||||||
creator_id: this.props.post.creator_id,
|
creator_id: this.props.post.creator_id,
|
||||||
url: this.props.post.url,
|
url: this.props.post.url,
|
||||||
|
nsfw: this.props.post.nsfw,
|
||||||
auth: null
|
auth: null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,6 +128,14 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
<div class="form-group row">
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" checked={this.state.postForm.nsfw} onChange={linkEvent(this, this.handlePostNsfwChange)}/>
|
||||||
|
<label class="form-check-label"><T i18nKey="nsfw">#</T></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<button type="submit" class="btn btn-secondary mr-2">
|
<button type="submit" class="btn btn-secondary mr-2">
|
||||||
|
@ -196,6 +206,11 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handlePostNsfwChange(i: PostForm, event: any) {
|
||||||
|
i.state.postForm.nsfw = event.target.checked;
|
||||||
|
i.setState(i.state);
|
||||||
|
}
|
||||||
|
|
||||||
handleCancel(i: PostForm) {
|
handleCancel(i: PostForm) {
|
||||||
i.props.onCancel();
|
i.props.onCancel();
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,6 +93,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
{post.locked &&
|
{post.locked &&
|
||||||
<small className="ml-2 text-muted font-italic"><T i18nKey="locked">#</T></small>
|
<small className="ml-2 text-muted font-italic"><T i18nKey="locked">#</T></small>
|
||||||
}
|
}
|
||||||
|
{post.nsfw &&
|
||||||
|
<small className="ml-2 text-muted font-italic"><T i18nKey="nsfw">#</T></small>
|
||||||
|
}
|
||||||
{ post.url && isImage(post.url) &&
|
{ post.url && isImage(post.url) &&
|
||||||
<>
|
<>
|
||||||
{ !this.state.imageExpanded
|
{ !this.state.imageExpanded
|
||||||
|
@ -251,6 +254,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
edit_id: i.props.post.id,
|
edit_id: i.props.post.id,
|
||||||
creator_id: i.props.post.creator_id,
|
creator_id: i.props.post.creator_id,
|
||||||
deleted: !i.props.post.deleted,
|
deleted: !i.props.post.deleted,
|
||||||
|
nsfw: i.props.post.nsfw,
|
||||||
auth: null
|
auth: null
|
||||||
};
|
};
|
||||||
WebSocketService.Instance.editPost(deleteForm);
|
WebSocketService.Instance.editPost(deleteForm);
|
||||||
|
@ -285,6 +289,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
creator_id: i.props.post.creator_id,
|
creator_id: i.props.post.creator_id,
|
||||||
removed: !i.props.post.removed,
|
removed: !i.props.post.removed,
|
||||||
reason: i.state.removeReason,
|
reason: i.state.removeReason,
|
||||||
|
nsfw: i.props.post.nsfw,
|
||||||
auth: null,
|
auth: null,
|
||||||
};
|
};
|
||||||
WebSocketService.Instance.editPost(form);
|
WebSocketService.Instance.editPost(form);
|
||||||
|
@ -299,6 +304,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
community_id: i.props.post.community_id,
|
community_id: i.props.post.community_id,
|
||||||
edit_id: i.props.post.id,
|
edit_id: i.props.post.id,
|
||||||
creator_id: i.props.post.creator_id,
|
creator_id: i.props.post.creator_id,
|
||||||
|
nsfw: i.props.post.nsfw,
|
||||||
locked: !i.props.post.locked,
|
locked: !i.props.post.locked,
|
||||||
auth: null,
|
auth: null,
|
||||||
};
|
};
|
||||||
|
|
|
@ -23,6 +23,7 @@ export class Setup extends Component<any, State> {
|
||||||
password: undefined,
|
password: undefined,
|
||||||
password_verify: undefined,
|
password_verify: undefined,
|
||||||
admin: true,
|
admin: true,
|
||||||
|
show_nsfw: true,
|
||||||
},
|
},
|
||||||
doneRegisteringUser: false,
|
doneRegisteringUser: false,
|
||||||
userLoading: false,
|
userLoading: false,
|
||||||
|
|
|
@ -2,8 +2,8 @@ import { Component, linkEvent } from 'inferno';
|
||||||
import { Link } from 'inferno-router';
|
import { Link } from 'inferno-router';
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
import { retryWhen, delay, take } from 'rxjs/operators';
|
import { retryWhen, delay, take } from 'rxjs/operators';
|
||||||
import { UserOperation, Post, Comment, CommunityUser, GetUserDetailsForm, SortType, UserDetailsResponse, UserView, CommentResponse } from '../interfaces';
|
import { UserOperation, Post, Comment, CommunityUser, GetUserDetailsForm, SortType, UserDetailsResponse, UserView, CommentResponse, UserSettingsForm, LoginResponse } from '../interfaces';
|
||||||
import { WebSocketService } from '../services';
|
import { WebSocketService, UserService } from '../services';
|
||||||
import { msgOp, fetchLimit, routeSortTypeToEnum, capitalizeFirstLetter } from '../utils';
|
import { msgOp, fetchLimit, routeSortTypeToEnum, capitalizeFirstLetter } from '../utils';
|
||||||
import { PostListing } from './post-listing';
|
import { PostListing } from './post-listing';
|
||||||
import { CommentNodes } from './comment-nodes';
|
import { CommentNodes } from './comment-nodes';
|
||||||
|
@ -28,6 +28,8 @@ interface UserState {
|
||||||
sort: SortType;
|
sort: SortType;
|
||||||
page: number;
|
page: number;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
|
userSettingsForm: UserSettingsForm;
|
||||||
|
userSettingsLoading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class User extends Component<any, UserState> {
|
export class User extends Component<any, UserState> {
|
||||||
|
@ -54,6 +56,11 @@ export class User extends Component<any, UserState> {
|
||||||
view: this.getViewFromProps(this.props),
|
view: this.getViewFromProps(this.props),
|
||||||
sort: this.getSortTypeFromProps(this.props),
|
sort: this.getSortTypeFromProps(this.props),
|
||||||
page: this.getPageFromProps(this.props),
|
page: this.getPageFromProps(this.props),
|
||||||
|
userSettingsForm: {
|
||||||
|
show_nsfw: null,
|
||||||
|
auth: null,
|
||||||
|
},
|
||||||
|
userSettingsLoading: null,
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
|
@ -75,6 +82,10 @@ export class User extends Component<any, UserState> {
|
||||||
this.refetch();
|
this.refetch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isCurrentUser() {
|
||||||
|
return UserService.Instance.user && UserService.Instance.user.id == this.state.user.id;
|
||||||
|
}
|
||||||
|
|
||||||
getViewFromProps(props: any): View {
|
getViewFromProps(props: any): View {
|
||||||
return (props.match.params.view) ?
|
return (props.match.params.view) ?
|
||||||
View[capitalizeFirstLetter(props.match.params.view)] :
|
View[capitalizeFirstLetter(props.match.params.view)] :
|
||||||
|
@ -131,6 +142,9 @@ export class User extends Component<any, UserState> {
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-3">
|
<div class="col-12 col-md-3">
|
||||||
{this.userInfo()}
|
{this.userInfo()}
|
||||||
|
{this.isCurrentUser &&
|
||||||
|
this.userSettings()
|
||||||
|
}
|
||||||
{this.moderates()}
|
{this.moderates()}
|
||||||
{this.follows()}
|
{this.follows()}
|
||||||
</div>
|
</div>
|
||||||
|
@ -219,7 +233,7 @@ export class User extends Component<any, UserState> {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h5>{user.name}</h5>
|
<h5>{user.name}</h5>
|
||||||
<div>{i18n.t('joined')}<MomentTime data={user} /></div>
|
<div>{i18n.t('joined')} <MomentTime data={user} /></div>
|
||||||
<table class="table table-bordered table-sm mt-2">
|
<table class="table table-bordered table-sm mt-2">
|
||||||
<tr>
|
<tr>
|
||||||
<td><T i18nKey="number_of_points" interpolation={{count: user.post_score}}>#</T></td>
|
<td><T i18nKey="number_of_points" interpolation={{count: user.post_score}}>#</T></td>
|
||||||
|
@ -235,6 +249,30 @@ export class User extends Component<any, UserState> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
userSettings() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h5><T i18nKey="settings">#</T></h5>
|
||||||
|
<form onSubmit={linkEvent(this, this.handleUserSettingsSubmit)}>
|
||||||
|
<div class="form-group row">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" checked={this.state.userSettingsForm.show_nsfw} onChange={linkEvent(this, this.handleUserSettingsShowNsfwChange)}/>
|
||||||
|
<label class="form-check-label"><T i18nKey="show_nsfw">#</T></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group row">
|
||||||
|
<div class="col-12">
|
||||||
|
<button type="submit" class="btn btn-secondary">{this.state.userSettingsLoading ?
|
||||||
|
<svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg> : capitalizeFirstLetter(i18n.t('save'))}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
moderates() {
|
moderates() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
@ -329,6 +367,19 @@ export class User extends Component<any, UserState> {
|
||||||
i.refetch();
|
i.refetch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleUserSettingsShowNsfwChange(i: User, event: any) {
|
||||||
|
i.state.userSettingsForm.show_nsfw = event.target.checked;
|
||||||
|
i.setState(i.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleUserSettingsSubmit(i: User, event: any) {
|
||||||
|
event.preventDefault();
|
||||||
|
i.state.userSettingsLoading = true;
|
||||||
|
i.setState(i.state);
|
||||||
|
|
||||||
|
WebSocketService.Instance.saveUserSettings(i.state.userSettingsForm);
|
||||||
|
}
|
||||||
|
|
||||||
parseMessage(msg: any) {
|
parseMessage(msg: any) {
|
||||||
console.log(msg);
|
console.log(msg);
|
||||||
let op: UserOperation = msgOp(msg);
|
let op: UserOperation = msgOp(msg);
|
||||||
|
@ -343,6 +394,9 @@ export class User extends Component<any, UserState> {
|
||||||
this.state.moderates = res.moderates;
|
this.state.moderates = res.moderates;
|
||||||
this.state.posts = res.posts;
|
this.state.posts = res.posts;
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
|
if (this.isCurrentUser) {
|
||||||
|
this.state.userSettingsForm.show_nsfw = UserService.Instance.user.show_nsfw;
|
||||||
|
}
|
||||||
document.title = `/u/${this.state.user.name} - ${WebSocketService.Instance.site.name}`;
|
document.title = `/u/${this.state.user.name} - ${WebSocketService.Instance.site.name}`;
|
||||||
window.scrollTo(0,0);
|
window.scrollTo(0,0);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
|
@ -378,6 +432,12 @@ export class User extends Component<any, UserState> {
|
||||||
if (res.comment.my_vote !== null)
|
if (res.comment.my_vote !== null)
|
||||||
found.my_vote = res.comment.my_vote;
|
found.my_vote = res.comment.my_vote;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
|
} else if (op == UserOperation.SaveUserSettings) {
|
||||||
|
this.state = this.emptyState;
|
||||||
|
this.state.userSettingsLoading = false;
|
||||||
|
this.setState(this.state);
|
||||||
|
let res: LoginResponse = msg;
|
||||||
|
UserService.Instance.login(res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
export enum UserOperation {
|
export enum UserOperation {
|
||||||
Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, SaveComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, SavePost, EditCommunity, FollowCommunity, GetFollowedCommunities, GetUserDetails, GetReplies, GetModlog, BanFromCommunity, AddModToCommunity, CreateSite, EditSite, GetSite, AddAdmin, BanUser, Search, MarkAllAsRead
|
Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, SaveComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, SavePost, EditCommunity, FollowCommunity, GetFollowedCommunities, GetUserDetails, GetReplies, GetModlog, BanFromCommunity, AddModToCommunity, CreateSite, EditSite, GetSite, AddAdmin, BanUser, Search, MarkAllAsRead, SaveUserSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum CommentSortType {
|
export enum CommentSortType {
|
||||||
|
@ -22,6 +22,7 @@ export interface User {
|
||||||
id: number;
|
id: number;
|
||||||
iss: string;
|
iss: string;
|
||||||
username: string;
|
username: string;
|
||||||
|
show_nsfw: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserView {
|
export interface UserView {
|
||||||
|
@ -53,6 +54,7 @@ export interface Community {
|
||||||
creator_id: number;
|
creator_id: number;
|
||||||
removed: boolean;
|
removed: boolean;
|
||||||
deleted: boolean;
|
deleted: boolean;
|
||||||
|
nsfw: boolean;
|
||||||
published: string;
|
published: string;
|
||||||
updated?: string;
|
updated?: string;
|
||||||
creator_name: string;
|
creator_name: string;
|
||||||
|
@ -74,11 +76,14 @@ export interface Post {
|
||||||
removed: boolean;
|
removed: boolean;
|
||||||
deleted: boolean;
|
deleted: boolean;
|
||||||
locked: boolean;
|
locked: boolean;
|
||||||
|
nsfw: boolean;
|
||||||
published: string;
|
published: string;
|
||||||
updated?: string;
|
updated?: string;
|
||||||
creator_name: string;
|
creator_name: string;
|
||||||
community_name: string;
|
community_name: string;
|
||||||
community_removed: boolean;
|
community_removed: boolean;
|
||||||
|
community_deleted: boolean;
|
||||||
|
community_nsfw: boolean;
|
||||||
number_of_comments: number;
|
number_of_comments: number;
|
||||||
score: number;
|
score: number;
|
||||||
upvotes: number;
|
upvotes: number;
|
||||||
|
@ -334,6 +339,7 @@ export interface RegisterForm {
|
||||||
password: string;
|
password: string;
|
||||||
password_verify: string;
|
password_verify: string;
|
||||||
admin: boolean;
|
admin: boolean;
|
||||||
|
show_nsfw: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LoginResponse {
|
export interface LoginResponse {
|
||||||
|
@ -341,7 +347,10 @@ export interface LoginResponse {
|
||||||
jwt: string;
|
jwt: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface UserSettingsForm {
|
||||||
|
show_nsfw: boolean;
|
||||||
|
auth: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface CommunityForm {
|
export interface CommunityForm {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -351,6 +360,7 @@ export interface CommunityForm {
|
||||||
edit_id?: number;
|
edit_id?: number;
|
||||||
removed?: boolean;
|
removed?: boolean;
|
||||||
deleted?: boolean;
|
deleted?: boolean;
|
||||||
|
nsfw: boolean;
|
||||||
reason?: string;
|
reason?: string;
|
||||||
expires?: number;
|
expires?: number;
|
||||||
auth?: string;
|
auth?: string;
|
||||||
|
@ -396,6 +406,7 @@ export interface PostForm {
|
||||||
creator_id: number;
|
creator_id: number;
|
||||||
removed?: boolean;
|
removed?: boolean;
|
||||||
deleted?: boolean;
|
deleted?: boolean;
|
||||||
|
nsfw: boolean;
|
||||||
locked?: boolean;
|
locked?: boolean;
|
||||||
reason?: string;
|
reason?: string;
|
||||||
auth: string;
|
auth: string;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { wsUri } from '../env';
|
import { wsUri } from '../env';
|
||||||
import { LoginForm, RegisterForm, UserOperation, CommunityForm, PostForm, SavePostForm, CommentForm, SaveCommentForm, CommentLikeForm, GetPostsForm, CreatePostLikeForm, FollowCommunityForm, GetUserDetailsForm, ListCommunitiesForm, GetModlogForm, BanFromCommunityForm, AddModToCommunityForm, AddAdminForm, BanUserForm, SiteForm, Site, UserView, GetRepliesForm, SearchForm } from '../interfaces';
|
import { LoginForm, RegisterForm, UserOperation, CommunityForm, PostForm, SavePostForm, CommentForm, SaveCommentForm, CommentLikeForm, GetPostsForm, CreatePostLikeForm, FollowCommunityForm, GetUserDetailsForm, ListCommunitiesForm, GetModlogForm, BanFromCommunityForm, AddModToCommunityForm, AddAdminForm, BanUserForm, SiteForm, Site, UserView, GetRepliesForm, SearchForm, UserSettingsForm } from '../interfaces';
|
||||||
import { webSocket } from 'rxjs/webSocket';
|
import { webSocket } from 'rxjs/webSocket';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { retryWhen, delay, take } from 'rxjs/operators';
|
import { retryWhen, delay, take } from 'rxjs/operators';
|
||||||
|
@ -184,6 +184,11 @@ export class WebSocketService {
|
||||||
this.subject.next(this.wsSendWrapper(UserOperation.MarkAllAsRead, form));
|
this.subject.next(this.wsSendWrapper(UserOperation.MarkAllAsRead, form));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public saveUserSettings(userSettingsForm: UserSettingsForm) {
|
||||||
|
this.setAuth(userSettingsForm);
|
||||||
|
this.subject.next(this.wsSendWrapper(UserOperation.SaveUserSettings, userSettingsForm));
|
||||||
|
}
|
||||||
|
|
||||||
private wsSendWrapper(op: UserOperation, data: any) {
|
private wsSendWrapper(op: UserOperation, data: any) {
|
||||||
let send = { op: UserOperation[op], data: data };
|
let send = { op: UserOperation[op], data: data };
|
||||||
console.log(send);
|
console.log(send);
|
||||||
|
|
|
@ -29,6 +29,7 @@ export const en = {
|
||||||
mod: 'mod',
|
mod: 'mod',
|
||||||
mods: 'mods',
|
mods: 'mods',
|
||||||
moderates: 'Moderates',
|
moderates: 'Moderates',
|
||||||
|
settings: 'Settings',
|
||||||
remove_as_mod: 'remove as mod',
|
remove_as_mod: 'remove as mod',
|
||||||
appoint_as_mod: 'appoint as mod',
|
appoint_as_mod: 'appoint as mod',
|
||||||
modlog: 'Modlog',
|
modlog: 'Modlog',
|
||||||
|
@ -112,6 +113,8 @@ export const en = {
|
||||||
setup_admin: 'Set Up Site Administrator',
|
setup_admin: 'Set Up Site Administrator',
|
||||||
your_site: 'your site',
|
your_site: 'your site',
|
||||||
modified: 'modified',
|
modified: 'modified',
|
||||||
|
nsfw: 'NSFW',
|
||||||
|
show_nsfw: 'Show NSFW content',
|
||||||
sponsors: 'Sponsors',
|
sponsors: 'Sponsors',
|
||||||
sponsors_of_lemmy: 'Sponsors of Lemmy',
|
sponsors_of_lemmy: 'Sponsors of 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:',
|
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:',
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"extends": "tslint:recommended",
|
"extends": "tslint:recommended",
|
||||||
"rules": {
|
"rules": {
|
||||||
"forin": false,
|
"forin": false,
|
||||||
"indent": [ true, "tabs" ],
|
"indent": [ true, "spaces" ],
|
||||||
"interface-name": false,
|
"interface-name": false,
|
||||||
"ban-types": true,
|
"ban-types": true,
|
||||||
"max-classes-per-file": true,
|
"max-classes-per-file": true,
|
||||||
|
|
|
@ -2773,7 +2773,7 @@ typescript@^2.6.2:
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c"
|
||||||
integrity sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==
|
integrity sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==
|
||||||
|
|
||||||
typescript@^3.3.3333:
|
typescript@^3.5.3:
|
||||||
version "3.5.3"
|
version "3.5.3"
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977"
|
||||||
integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==
|
integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==
|
||||||
|
|
Loading…
Reference in a new issue