mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-11-30 00:01:25 +00:00
Saving replies, the actual fixes will be in the merge to dev.
This commit is contained in:
parent
6f801bb819
commit
9afadfb9c4
42 changed files with 1017 additions and 432 deletions
|
@ -38,7 +38,7 @@ create table community (
|
||||||
description text,
|
description text,
|
||||||
category_id int references category on update cascade on delete cascade not null,
|
category_id int references category on update cascade on delete cascade not null,
|
||||||
creator_id int references user_ on update cascade on delete cascade not null,
|
creator_id int references user_ on update cascade on delete cascade not null,
|
||||||
removed boolean default false,
|
removed boolean default false not null,
|
||||||
published timestamp not null default now(),
|
published timestamp not null default now(),
|
||||||
updated timestamp
|
updated timestamp
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
|
drop table post_read;
|
||||||
|
drop table post_saved;
|
||||||
drop table post_like;
|
drop table post_like;
|
||||||
drop table post;
|
drop table post;
|
||||||
|
|
|
@ -5,8 +5,8 @@ create table post (
|
||||||
body text,
|
body text,
|
||||||
creator_id int references user_ on update cascade on delete cascade not null,
|
creator_id int references user_ on update cascade on delete cascade not null,
|
||||||
community_id int references community on update cascade on delete cascade not null,
|
community_id int references community on update cascade on delete cascade not null,
|
||||||
removed boolean default false,
|
removed boolean default false not null,
|
||||||
locked boolean default false,
|
locked boolean default false not null,
|
||||||
published timestamp not null default now(),
|
published timestamp not null default now(),
|
||||||
updated timestamp
|
updated timestamp
|
||||||
);
|
);
|
||||||
|
@ -20,3 +20,18 @@ create table post_like (
|
||||||
unique(post_id, user_id)
|
unique(post_id, user_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
create table post_saved (
|
||||||
|
id serial primary key,
|
||||||
|
post_id int references post on update cascade on delete cascade not null,
|
||||||
|
user_id int references user_ on update cascade on delete cascade not null,
|
||||||
|
published timestamp not null default now(),
|
||||||
|
unique(post_id, user_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
create table post_read (
|
||||||
|
id serial primary key,
|
||||||
|
post_id int references post on update cascade on delete cascade not null,
|
||||||
|
user_id int references user_ on update cascade on delete cascade not null,
|
||||||
|
published timestamp not null default now(),
|
||||||
|
unique(post_id, user_id)
|
||||||
|
);
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
|
drop table comment_saved;
|
||||||
drop table comment_like;
|
drop table comment_like;
|
||||||
drop table comment;
|
drop table comment;
|
||||||
|
|
|
@ -4,7 +4,8 @@ create table comment (
|
||||||
post_id int references post on update cascade on delete cascade not null,
|
post_id int references post on update cascade on delete cascade not null,
|
||||||
parent_id int references comment on update cascade on delete cascade,
|
parent_id int references comment on update cascade on delete cascade,
|
||||||
content text not null,
|
content text not null,
|
||||||
removed boolean default false,
|
removed boolean default false not null,
|
||||||
|
read boolean default false not null,
|
||||||
published timestamp not null default now(),
|
published timestamp not null default now(),
|
||||||
updated timestamp
|
updated timestamp
|
||||||
);
|
);
|
||||||
|
@ -18,3 +19,11 @@ create table comment_like (
|
||||||
published timestamp not null default now(),
|
published timestamp not null default now(),
|
||||||
unique(comment_id, user_id)
|
unique(comment_id, user_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
create table comment_saved (
|
||||||
|
id serial primary key,
|
||||||
|
comment_id int references comment on update cascade on delete cascade not null,
|
||||||
|
user_id int references user_ on update cascade on delete cascade not null,
|
||||||
|
published timestamp not null default now(),
|
||||||
|
unique(comment_id, user_id)
|
||||||
|
);
|
||||||
|
|
|
@ -31,7 +31,8 @@ ap.*,
|
||||||
u.id as user_id,
|
u.id as user_id,
|
||||||
coalesce(pl.score, 0) as my_vote,
|
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 cf.id::bool from community_follower cf where u.id = cf.user_id and cf.community_id = ap.community_id) as subscribed,
|
||||||
u.admin or (select cm.id::bool from community_moderator cm where u.id = cm.user_id and cm.community_id = ap.community_id) as am_mod
|
(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
|
from user_ u
|
||||||
cross join all_post ap
|
cross join all_post ap
|
||||||
left join post_like pl on u.id = pl.user_id and ap.id = pl.post_id
|
left join post_like pl on u.id = pl.user_id and ap.id = pl.post_id
|
||||||
|
@ -43,6 +44,7 @@ ap.*,
|
||||||
null as user_id,
|
null as user_id,
|
||||||
null as my_vote,
|
null as my_vote,
|
||||||
null as subscribed,
|
null as subscribed,
|
||||||
null as am_mod
|
null as read,
|
||||||
|
null as saved
|
||||||
from all_post ap
|
from all_post ap
|
||||||
;
|
;
|
||||||
|
|
|
@ -13,19 +13,16 @@ with all_community as
|
||||||
select
|
select
|
||||||
ac.*,
|
ac.*,
|
||||||
u.id as user_id,
|
u.id as user_id,
|
||||||
cf.id::boolean as subscribed,
|
(select cf.id::boolean from community_follower cf where u.id = cf.user_id and ac.id = cf.community_id) as subscribed
|
||||||
u.admin or (select cm.id::bool from community_moderator cm where u.id = cm.user_id and cm.community_id = ac.id) as am_mod
|
|
||||||
from user_ u
|
from user_ u
|
||||||
cross join all_community ac
|
cross join all_community ac
|
||||||
left join community_follower cf on u.id = cf.user_id and ac.id = cf.community_id
|
|
||||||
|
|
||||||
union all
|
union all
|
||||||
|
|
||||||
select
|
select
|
||||||
ac.*,
|
ac.*,
|
||||||
null as user_id,
|
null as user_id,
|
||||||
null as subscribed,
|
null as subscribed
|
||||||
null as am_mod
|
|
||||||
from all_community ac
|
from all_community ac
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,8 @@ with all_comment as
|
||||||
select
|
select
|
||||||
c.*,
|
c.*,
|
||||||
(select community_id from post p where p.id = c.post_id),
|
(select community_id from post p where p.id = c.post_id),
|
||||||
(select cb.id::bool from community_user_ban cb where c.creator_id = cb.user_id) as banned,
|
(select u.banned from user_ u where c.creator_id = u.id) as banned,
|
||||||
|
(select cb.id::bool from community_user_ban cb, post p where c.creator_id = cb.user_id and p.id = c.post_id and p.community_id = cb.community_id) as banned_from_community,
|
||||||
(select name from user_ where c.creator_id = user_.id) as creator_name,
|
(select name from user_ where c.creator_id = user_.id) as creator_name,
|
||||||
coalesce(sum(cl.score), 0) as score,
|
coalesce(sum(cl.score), 0) as score,
|
||||||
count (case when cl.score = 1 then 1 else null end) as upvotes,
|
count (case when cl.score = 1 then 1 else null end) as upvotes,
|
||||||
|
@ -18,7 +19,7 @@ select
|
||||||
ac.*,
|
ac.*,
|
||||||
u.id as user_id,
|
u.id as user_id,
|
||||||
coalesce(cl.score, 0) as my_vote,
|
coalesce(cl.score, 0) as my_vote,
|
||||||
u.admin or (select cm.id::bool from community_moderator cm, post p where u.id = cm.user_id and ac.post_id = p.id and p.community_id = cm.community_id) as am_mod
|
(select cs.id::bool from comment_saved cs where u.id = cs.user_id and cs.comment_id = ac.id) as saved
|
||||||
from user_ u
|
from user_ u
|
||||||
cross join all_comment ac
|
cross join all_comment ac
|
||||||
left join comment_like cl on u.id = cl.user_id and ac.id = cl.comment_id
|
left join comment_like cl on u.id = cl.user_id and ac.id = cl.comment_id
|
||||||
|
@ -29,6 +30,6 @@ select
|
||||||
ac.*,
|
ac.*,
|
||||||
null as user_id,
|
null as user_id,
|
||||||
null as my_vote,
|
null as my_vote,
|
||||||
null as am_mod
|
null as saved
|
||||||
from all_comment ac
|
from all_comment ac
|
||||||
;
|
;
|
||||||
|
|
|
@ -43,8 +43,7 @@ create view mod_ban_view as
|
||||||
select mb.*,
|
select mb.*,
|
||||||
(select name from user_ u where mb.mod_user_id = u.id) as mod_user_name,
|
(select name from user_ u where mb.mod_user_id = u.id) as mod_user_name,
|
||||||
(select name from user_ u where mb.other_user_id = u.id) as other_user_name
|
(select name from user_ u where mb.other_user_id = u.id) as other_user_name
|
||||||
from mod_ban_from_community mb;
|
from mod_ban mb;
|
||||||
|
|
||||||
|
|
||||||
create view mod_add_community_view as
|
create view mod_add_community_view as
|
||||||
select ma.*,
|
select ma.*,
|
||||||
|
@ -53,7 +52,6 @@ select ma.*,
|
||||||
(select name from community c where ma.community_id = c.id) as community_name
|
(select name from community c where ma.community_id = c.id) as community_name
|
||||||
from mod_add_community ma;
|
from mod_add_community ma;
|
||||||
|
|
||||||
|
|
||||||
create view mod_add_view as
|
create view mod_add_view as
|
||||||
select ma.*,
|
select ma.*,
|
||||||
(select name from user_ u where ma.mod_user_id = u.id) as mod_user_name,
|
(select name from user_ u where ma.mod_user_id = u.id) as mod_user_name,
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
extern crate diesel;
|
extern crate diesel;
|
||||||
use schema::{comment, comment_like};
|
use schema::{comment, comment_like, comment_saved};
|
||||||
use diesel::*;
|
use diesel::*;
|
||||||
use diesel::result::Error;
|
use diesel::result::Error;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use {Crud, Likeable};
|
use {Crud, Likeable, Saveable};
|
||||||
use actions::post::Post;
|
use actions::post::Post;
|
||||||
|
|
||||||
// WITH RECURSIVE MyTree AS (
|
// WITH RECURSIVE MyTree AS (
|
||||||
|
@ -22,7 +22,8 @@ pub struct Comment {
|
||||||
pub post_id: i32,
|
pub post_id: i32,
|
||||||
pub parent_id: Option<i32>,
|
pub parent_id: Option<i32>,
|
||||||
pub content: String,
|
pub content: String,
|
||||||
pub removed: Option<bool>,
|
pub removed: bool,
|
||||||
|
pub read: bool,
|
||||||
pub published: chrono::NaiveDateTime,
|
pub published: chrono::NaiveDateTime,
|
||||||
pub updated: Option<chrono::NaiveDateTime>
|
pub updated: Option<chrono::NaiveDateTime>
|
||||||
}
|
}
|
||||||
|
@ -38,27 +39,6 @@ pub struct CommentForm {
|
||||||
pub updated: Option<chrono::NaiveDateTime>
|
pub updated: Option<chrono::NaiveDateTime>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug, Clone)]
|
|
||||||
#[belongs_to(Comment)]
|
|
||||||
#[table_name = "comment_like"]
|
|
||||||
pub struct CommentLike {
|
|
||||||
pub id: i32,
|
|
||||||
pub user_id: i32,
|
|
||||||
pub comment_id: i32,
|
|
||||||
pub post_id: i32,
|
|
||||||
pub score: i16,
|
|
||||||
pub published: chrono::NaiveDateTime,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Insertable, AsChangeset, Clone)]
|
|
||||||
#[table_name="comment_like"]
|
|
||||||
pub struct CommentLikeForm {
|
|
||||||
pub user_id: i32,
|
|
||||||
pub comment_id: i32,
|
|
||||||
pub post_id: i32,
|
|
||||||
pub score: i16
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Crud<CommentForm> for Comment {
|
impl Crud<CommentForm> for Comment {
|
||||||
fn read(conn: &PgConnection, comment_id: i32) -> Result<Self, Error> {
|
fn read(conn: &PgConnection, comment_id: i32) -> Result<Self, Error> {
|
||||||
use schema::comment::dsl::*;
|
use schema::comment::dsl::*;
|
||||||
|
@ -87,6 +67,27 @@ impl Crud<CommentForm> for Comment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug, Clone)]
|
||||||
|
#[belongs_to(Comment)]
|
||||||
|
#[table_name = "comment_like"]
|
||||||
|
pub struct CommentLike {
|
||||||
|
pub id: i32,
|
||||||
|
pub user_id: i32,
|
||||||
|
pub comment_id: i32,
|
||||||
|
pub post_id: i32,
|
||||||
|
pub score: i16,
|
||||||
|
pub published: chrono::NaiveDateTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Insertable, AsChangeset, Clone)]
|
||||||
|
#[table_name="comment_like"]
|
||||||
|
pub struct CommentLikeForm {
|
||||||
|
pub user_id: i32,
|
||||||
|
pub comment_id: i32,
|
||||||
|
pub post_id: i32,
|
||||||
|
pub score: i16
|
||||||
|
}
|
||||||
|
|
||||||
impl Likeable <CommentLikeForm> for CommentLike {
|
impl Likeable <CommentLikeForm> for CommentLike {
|
||||||
fn read(conn: &PgConnection, comment_id_from: i32) -> Result<Vec<Self>, Error> {
|
fn read(conn: &PgConnection, comment_id_from: i32) -> Result<Vec<Self>, Error> {
|
||||||
use schema::comment_like::dsl::*;
|
use schema::comment_like::dsl::*;
|
||||||
|
@ -119,6 +120,39 @@ impl CommentLike {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
|
||||||
|
#[belongs_to(Comment)]
|
||||||
|
#[table_name = "comment_saved"]
|
||||||
|
pub struct CommentSaved {
|
||||||
|
pub id: i32,
|
||||||
|
pub comment_id: i32,
|
||||||
|
pub user_id: i32,
|
||||||
|
pub published: chrono::NaiveDateTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Insertable, AsChangeset, Clone)]
|
||||||
|
#[table_name="comment_saved"]
|
||||||
|
pub struct CommentSavedForm {
|
||||||
|
pub comment_id: i32,
|
||||||
|
pub user_id: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Saveable <CommentSavedForm> for CommentSaved {
|
||||||
|
fn save(conn: &PgConnection, comment_saved_form: &CommentSavedForm) -> Result<Self, Error> {
|
||||||
|
use schema::comment_saved::dsl::*;
|
||||||
|
insert_into(comment_saved)
|
||||||
|
.values(comment_saved_form)
|
||||||
|
.get_result::<Self>(conn)
|
||||||
|
}
|
||||||
|
fn unsave(conn: &PgConnection, comment_saved_form: &CommentSavedForm) -> Result<usize, Error> {
|
||||||
|
use schema::comment_saved::dsl::*;
|
||||||
|
diesel::delete(comment_saved
|
||||||
|
.filter(comment_id.eq(comment_saved_form.comment_id))
|
||||||
|
.filter(user_id.eq(comment_saved_form.user_id)))
|
||||||
|
.execute(conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use establish_connection;
|
use establish_connection;
|
||||||
|
@ -150,7 +184,7 @@ mod tests {
|
||||||
description: None,
|
description: None,
|
||||||
category_id: 1,
|
category_id: 1,
|
||||||
creator_id: inserted_user.id,
|
creator_id: inserted_user.id,
|
||||||
removed: None,
|
removed: false,
|
||||||
updated: None
|
updated: None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -162,8 +196,8 @@ mod tests {
|
||||||
url: None,
|
url: None,
|
||||||
body: None,
|
body: None,
|
||||||
community_id: inserted_community.id,
|
community_id: inserted_community.id,
|
||||||
removed: None,
|
removed: false,
|
||||||
locked: None,
|
locked: false,
|
||||||
updated: None
|
updated: None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -185,7 +219,8 @@ mod tests {
|
||||||
content: "A test comment".into(),
|
content: "A test comment".into(),
|
||||||
creator_id: inserted_user.id,
|
creator_id: inserted_user.id,
|
||||||
post_id: inserted_post.id,
|
post_id: inserted_post.id,
|
||||||
removed: Some(false),
|
removed: false,
|
||||||
|
read: false,
|
||||||
parent_id: None,
|
parent_id: None,
|
||||||
published: inserted_comment.published,
|
published: inserted_comment.published,
|
||||||
updated: None
|
updated: None
|
||||||
|
@ -202,6 +237,7 @@ mod tests {
|
||||||
|
|
||||||
let inserted_child_comment = Comment::create(&conn, &child_comment_form).unwrap();
|
let inserted_child_comment = Comment::create(&conn, &child_comment_form).unwrap();
|
||||||
|
|
||||||
|
// Comment Like
|
||||||
let comment_like_form = CommentLikeForm {
|
let comment_like_form = CommentLikeForm {
|
||||||
comment_id: inserted_comment.id,
|
comment_id: inserted_comment.id,
|
||||||
post_id: inserted_post.id,
|
post_id: inserted_post.id,
|
||||||
|
@ -220,9 +256,25 @@ mod tests {
|
||||||
score: 1
|
score: 1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Comment Saved
|
||||||
|
let comment_saved_form = CommentSavedForm {
|
||||||
|
comment_id: inserted_comment.id,
|
||||||
|
user_id: inserted_user.id,
|
||||||
|
};
|
||||||
|
|
||||||
|
let inserted_comment_saved = CommentSaved::save(&conn, &comment_saved_form).unwrap();
|
||||||
|
|
||||||
|
let expected_comment_saved = CommentSaved {
|
||||||
|
id: inserted_comment_saved.id,
|
||||||
|
comment_id: inserted_comment.id,
|
||||||
|
user_id: inserted_user.id,
|
||||||
|
published: inserted_comment_saved.published,
|
||||||
|
};
|
||||||
|
|
||||||
let read_comment = Comment::read(&conn, inserted_comment.id).unwrap();
|
let read_comment = Comment::read(&conn, inserted_comment.id).unwrap();
|
||||||
let updated_comment = Comment::update(&conn, inserted_comment.id, &comment_form).unwrap();
|
let updated_comment = Comment::update(&conn, inserted_comment.id, &comment_form).unwrap();
|
||||||
let like_removed = CommentLike::remove(&conn, &comment_like_form).unwrap();
|
let like_removed = CommentLike::remove(&conn, &comment_like_form).unwrap();
|
||||||
|
let saved_removed = CommentSaved::unsave(&conn, &comment_saved_form).unwrap();
|
||||||
let num_deleted = Comment::delete(&conn, inserted_comment.id).unwrap();
|
let num_deleted = Comment::delete(&conn, inserted_comment.id).unwrap();
|
||||||
Comment::delete(&conn, inserted_child_comment.id).unwrap();
|
Comment::delete(&conn, inserted_child_comment.id).unwrap();
|
||||||
Post::delete(&conn, inserted_post.id).unwrap();
|
Post::delete(&conn, inserted_post.id).unwrap();
|
||||||
|
@ -233,8 +285,10 @@ mod tests {
|
||||||
assert_eq!(expected_comment, inserted_comment);
|
assert_eq!(expected_comment, inserted_comment);
|
||||||
assert_eq!(expected_comment, updated_comment);
|
assert_eq!(expected_comment, updated_comment);
|
||||||
assert_eq!(expected_comment_like, inserted_comment_like);
|
assert_eq!(expected_comment_like, inserted_comment_like);
|
||||||
|
assert_eq!(expected_comment_saved, inserted_comment_saved);
|
||||||
assert_eq!(expected_comment.id, inserted_child_comment.parent_id.unwrap());
|
assert_eq!(expected_comment.id, inserted_child_comment.parent_id.unwrap());
|
||||||
assert_eq!(1, like_removed);
|
assert_eq!(1, like_removed);
|
||||||
|
assert_eq!(1, saved_removed);
|
||||||
assert_eq!(1, num_deleted);
|
assert_eq!(1, num_deleted);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,18 +13,20 @@ table! {
|
||||||
post_id -> Int4,
|
post_id -> Int4,
|
||||||
parent_id -> Nullable<Int4>,
|
parent_id -> Nullable<Int4>,
|
||||||
content -> Text,
|
content -> Text,
|
||||||
removed -> Nullable<Bool>,
|
removed -> Bool,
|
||||||
|
read -> Bool,
|
||||||
published -> Timestamp,
|
published -> Timestamp,
|
||||||
updated -> Nullable<Timestamp>,
|
updated -> Nullable<Timestamp>,
|
||||||
community_id -> Int4,
|
community_id -> Int4,
|
||||||
banned -> Nullable<Bool>,
|
banned -> Bool,
|
||||||
|
banned_from_community -> Bool,
|
||||||
creator_name -> Varchar,
|
creator_name -> Varchar,
|
||||||
score -> BigInt,
|
score -> BigInt,
|
||||||
upvotes -> BigInt,
|
upvotes -> BigInt,
|
||||||
downvotes -> BigInt,
|
downvotes -> BigInt,
|
||||||
user_id -> Nullable<Int4>,
|
user_id -> Nullable<Int4>,
|
||||||
my_vote -> Nullable<Int4>,
|
my_vote -> Nullable<Int4>,
|
||||||
am_mod -> Nullable<Bool>,
|
saved -> Nullable<Bool>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,18 +38,20 @@ pub struct CommentView {
|
||||||
pub post_id: i32,
|
pub post_id: i32,
|
||||||
pub parent_id: Option<i32>,
|
pub parent_id: Option<i32>,
|
||||||
pub content: String,
|
pub content: String,
|
||||||
pub removed: Option<bool>,
|
pub removed: bool,
|
||||||
|
pub read: bool,
|
||||||
pub published: chrono::NaiveDateTime,
|
pub published: chrono::NaiveDateTime,
|
||||||
pub updated: Option<chrono::NaiveDateTime>,
|
pub updated: Option<chrono::NaiveDateTime>,
|
||||||
pub community_id: i32,
|
pub community_id: i32,
|
||||||
pub banned: Option<bool>,
|
pub banned: bool,
|
||||||
|
pub banned_from_community: bool,
|
||||||
pub creator_name: String,
|
pub creator_name: String,
|
||||||
pub score: i64,
|
pub score: i64,
|
||||||
pub upvotes: i64,
|
pub upvotes: i64,
|
||||||
pub downvotes: i64,
|
pub downvotes: i64,
|
||||||
pub user_id: Option<i32>,
|
pub user_id: Option<i32>,
|
||||||
pub my_vote: Option<i32>,
|
pub my_vote: Option<i32>,
|
||||||
pub am_mod: Option<bool>,
|
pub saved: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CommentView {
|
impl CommentView {
|
||||||
|
@ -57,6 +61,7 @@ impl CommentView {
|
||||||
for_post_id: Option<i32>,
|
for_post_id: Option<i32>,
|
||||||
for_creator_id: Option<i32>,
|
for_creator_id: Option<i32>,
|
||||||
my_user_id: Option<i32>,
|
my_user_id: Option<i32>,
|
||||||
|
saved_only: bool,
|
||||||
page: Option<i64>,
|
page: Option<i64>,
|
||||||
limit: Option<i64>,
|
limit: Option<i64>,
|
||||||
) -> Result<Vec<Self>, Error> {
|
) -> Result<Vec<Self>, Error> {
|
||||||
|
@ -82,6 +87,10 @@ impl CommentView {
|
||||||
query = query.filter(post_id.eq(for_post_id));
|
query = query.filter(post_id.eq(for_post_id));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if saved_only {
|
||||||
|
query = query.filter(saved.eq(true));
|
||||||
|
}
|
||||||
|
|
||||||
query = match sort {
|
query = match sort {
|
||||||
// SortType::Hot => query.order_by(hot_rank.desc()),
|
// SortType::Hot => query.order_by(hot_rank.desc()),
|
||||||
SortType::New => query.order_by(published.desc()),
|
SortType::New => query.order_by(published.desc()),
|
||||||
|
@ -159,7 +168,7 @@ mod tests {
|
||||||
description: None,
|
description: None,
|
||||||
category_id: 1,
|
category_id: 1,
|
||||||
creator_id: inserted_user.id,
|
creator_id: inserted_user.id,
|
||||||
removed: None,
|
removed: false,
|
||||||
updated: None
|
updated: None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -171,8 +180,8 @@ mod tests {
|
||||||
url: None,
|
url: None,
|
||||||
body: None,
|
body: None,
|
||||||
community_id: inserted_community.id,
|
community_id: inserted_community.id,
|
||||||
removed: None,
|
removed: false,
|
||||||
locked: None,
|
locked: false,
|
||||||
updated: None
|
updated: None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -205,8 +214,10 @@ mod tests {
|
||||||
post_id: inserted_post.id,
|
post_id: inserted_post.id,
|
||||||
community_id: inserted_community.id,
|
community_id: inserted_community.id,
|
||||||
parent_id: None,
|
parent_id: None,
|
||||||
removed: Some(false),
|
removed: false,
|
||||||
banned: None,
|
read: false,
|
||||||
|
banned: false,
|
||||||
|
banned_from_community: false,
|
||||||
published: inserted_comment.published,
|
published: inserted_comment.published,
|
||||||
updated: None,
|
updated: None,
|
||||||
creator_name: inserted_user.name.to_owned(),
|
creator_name: inserted_user.name.to_owned(),
|
||||||
|
@ -215,7 +226,7 @@ mod tests {
|
||||||
upvotes: 1,
|
upvotes: 1,
|
||||||
user_id: None,
|
user_id: None,
|
||||||
my_vote: None,
|
my_vote: None,
|
||||||
am_mod: None,
|
saved: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let expected_comment_view_with_user = CommentView {
|
let expected_comment_view_with_user = CommentView {
|
||||||
|
@ -225,8 +236,10 @@ mod tests {
|
||||||
post_id: inserted_post.id,
|
post_id: inserted_post.id,
|
||||||
community_id: inserted_community.id,
|
community_id: inserted_community.id,
|
||||||
parent_id: None,
|
parent_id: None,
|
||||||
removed: Some(false),
|
removed: false,
|
||||||
banned: None,
|
read: false,
|
||||||
|
banned: false,
|
||||||
|
banned_from_community: false,
|
||||||
published: inserted_comment.published,
|
published: inserted_comment.published,
|
||||||
updated: None,
|
updated: None,
|
||||||
creator_name: inserted_user.name.to_owned(),
|
creator_name: inserted_user.name.to_owned(),
|
||||||
|
@ -235,11 +248,11 @@ mod tests {
|
||||||
upvotes: 1,
|
upvotes: 1,
|
||||||
user_id: Some(inserted_user.id),
|
user_id: Some(inserted_user.id),
|
||||||
my_vote: Some(1),
|
my_vote: Some(1),
|
||||||
am_mod: None,
|
saved: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let read_comment_views_no_user = CommentView::list(&conn, &SortType::New, Some(inserted_post.id), None, None, None, None).unwrap();
|
let read_comment_views_no_user = CommentView::list(&conn, &SortType::New, Some(inserted_post.id), None, None, false, None, None).unwrap();
|
||||||
let read_comment_views_with_user = CommentView::list(&conn, &SortType::New, Some(inserted_post.id), None, Some(inserted_user.id), None, None).unwrap();
|
let read_comment_views_with_user = CommentView::list(&conn, &SortType::New, Some(inserted_post.id), None, Some(inserted_user.id), false, None, None).unwrap();
|
||||||
let like_removed = CommentLike::remove(&conn, &comment_like_form).unwrap();
|
let like_removed = CommentLike::remove(&conn, &comment_like_form).unwrap();
|
||||||
let num_deleted = Comment::delete(&conn, inserted_comment.id).unwrap();
|
let num_deleted = Comment::delete(&conn, inserted_comment.id).unwrap();
|
||||||
Post::delete(&conn, inserted_post.id).unwrap();
|
Post::delete(&conn, inserted_post.id).unwrap();
|
||||||
|
|
|
@ -14,7 +14,7 @@ pub struct Community {
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub category_id: i32,
|
pub category_id: i32,
|
||||||
pub creator_id: i32,
|
pub creator_id: i32,
|
||||||
pub removed: Option<bool>,
|
pub removed: bool,
|
||||||
pub published: chrono::NaiveDateTime,
|
pub published: chrono::NaiveDateTime,
|
||||||
pub updated: Option<chrono::NaiveDateTime>
|
pub updated: Option<chrono::NaiveDateTime>
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ pub struct CommunityForm {
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub category_id: i32,
|
pub category_id: i32,
|
||||||
pub creator_id: i32,
|
pub creator_id: i32,
|
||||||
pub removed: Option<bool>,
|
pub removed: bool,
|
||||||
pub updated: Option<chrono::NaiveDateTime>
|
pub updated: Option<chrono::NaiveDateTime>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,7 +236,7 @@ mod tests {
|
||||||
title: "nada".to_owned(),
|
title: "nada".to_owned(),
|
||||||
description: None,
|
description: None,
|
||||||
category_id: 1,
|
category_id: 1,
|
||||||
removed: None,
|
removed: false,
|
||||||
updated: None,
|
updated: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -249,7 +249,7 @@ mod tests {
|
||||||
title: "nada".to_owned(),
|
title: "nada".to_owned(),
|
||||||
description: None,
|
description: None,
|
||||||
category_id: 1,
|
category_id: 1,
|
||||||
removed: Some(false),
|
removed: false,
|
||||||
published: inserted_community.published,
|
published: inserted_community.published,
|
||||||
updated: None
|
updated: None
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,7 +12,7 @@ table! {
|
||||||
description -> Nullable<Text>,
|
description -> Nullable<Text>,
|
||||||
category_id -> Int4,
|
category_id -> Int4,
|
||||||
creator_id -> Int4,
|
creator_id -> Int4,
|
||||||
removed -> Nullable<Bool>,
|
removed -> Bool,
|
||||||
published -> Timestamp,
|
published -> Timestamp,
|
||||||
updated -> Nullable<Timestamp>,
|
updated -> Nullable<Timestamp>,
|
||||||
creator_name -> Varchar,
|
creator_name -> Varchar,
|
||||||
|
@ -22,7 +22,6 @@ table! {
|
||||||
number_of_comments -> BigInt,
|
number_of_comments -> BigInt,
|
||||||
user_id -> Nullable<Int4>,
|
user_id -> Nullable<Int4>,
|
||||||
subscribed -> Nullable<Bool>,
|
subscribed -> Nullable<Bool>,
|
||||||
am_mod -> Nullable<Bool>,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +82,7 @@ pub struct CommunityView {
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub category_id: i32,
|
pub category_id: i32,
|
||||||
pub creator_id: i32,
|
pub creator_id: i32,
|
||||||
pub removed: Option<bool>,
|
pub removed: bool,
|
||||||
pub published: chrono::NaiveDateTime,
|
pub published: chrono::NaiveDateTime,
|
||||||
pub updated: Option<chrono::NaiveDateTime>,
|
pub updated: Option<chrono::NaiveDateTime>,
|
||||||
pub creator_name: String,
|
pub creator_name: String,
|
||||||
|
@ -93,7 +92,6 @@ pub struct CommunityView {
|
||||||
pub number_of_comments: i64,
|
pub number_of_comments: i64,
|
||||||
pub user_id: Option<i32>,
|
pub user_id: Option<i32>,
|
||||||
pub subscribed: Option<bool>,
|
pub subscribed: Option<bool>,
|
||||||
pub am_mod: Option<bool>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CommunityView {
|
impl CommunityView {
|
||||||
|
|
|
@ -441,7 +441,7 @@ mod tests {
|
||||||
description: None,
|
description: None,
|
||||||
category_id: 1,
|
category_id: 1,
|
||||||
creator_id: inserted_user.id,
|
creator_id: inserted_user.id,
|
||||||
removed: None,
|
removed: false,
|
||||||
updated: None
|
updated: None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -453,8 +453,8 @@ mod tests {
|
||||||
body: None,
|
body: None,
|
||||||
creator_id: inserted_user.id,
|
creator_id: inserted_user.id,
|
||||||
community_id: inserted_community.id,
|
community_id: inserted_community.id,
|
||||||
removed: None,
|
removed: false,
|
||||||
locked: None,
|
locked: false,
|
||||||
updated: None
|
updated: None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
extern crate diesel;
|
extern crate diesel;
|
||||||
use schema::{post, post_like};
|
use schema::{post, post_like, post_saved, post_read};
|
||||||
use diesel::*;
|
use diesel::*;
|
||||||
use diesel::result::Error;
|
use diesel::result::Error;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use {Crud, Likeable};
|
use {Crud, Likeable, Saveable, Readable};
|
||||||
|
|
||||||
#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
|
#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
|
||||||
#[table_name="post"]
|
#[table_name="post"]
|
||||||
|
@ -14,8 +14,8 @@ pub struct Post {
|
||||||
pub body: Option<String>,
|
pub body: Option<String>,
|
||||||
pub creator_id: i32,
|
pub creator_id: i32,
|
||||||
pub community_id: i32,
|
pub community_id: i32,
|
||||||
pub removed: Option<bool>,
|
pub removed: bool,
|
||||||
pub locked: Option<bool>,
|
pub locked: bool,
|
||||||
pub published: chrono::NaiveDateTime,
|
pub published: chrono::NaiveDateTime,
|
||||||
pub updated: Option<chrono::NaiveDateTime>
|
pub updated: Option<chrono::NaiveDateTime>
|
||||||
}
|
}
|
||||||
|
@ -28,30 +28,11 @@ pub struct PostForm {
|
||||||
pub body: Option<String>,
|
pub body: Option<String>,
|
||||||
pub creator_id: i32,
|
pub creator_id: i32,
|
||||||
pub community_id: i32,
|
pub community_id: i32,
|
||||||
pub removed: Option<bool>,
|
pub removed: bool,
|
||||||
pub locked: Option<bool>,
|
pub locked: bool,
|
||||||
pub updated: Option<chrono::NaiveDateTime>
|
pub updated: Option<chrono::NaiveDateTime>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
|
|
||||||
#[belongs_to(Post)]
|
|
||||||
#[table_name = "post_like"]
|
|
||||||
pub struct PostLike {
|
|
||||||
pub id: i32,
|
|
||||||
pub post_id: i32,
|
|
||||||
pub user_id: i32,
|
|
||||||
pub score: i16,
|
|
||||||
pub published: chrono::NaiveDateTime,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Insertable, AsChangeset, Clone)]
|
|
||||||
#[table_name="post_like"]
|
|
||||||
pub struct PostLikeForm {
|
|
||||||
pub post_id: i32,
|
|
||||||
pub user_id: i32,
|
|
||||||
pub score: i16
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Crud<PostForm> for Post {
|
impl Crud<PostForm> for Post {
|
||||||
fn read(conn: &PgConnection, post_id: i32) -> Result<Self, Error> {
|
fn read(conn: &PgConnection, post_id: i32) -> Result<Self, Error> {
|
||||||
use schema::post::dsl::*;
|
use schema::post::dsl::*;
|
||||||
|
@ -80,6 +61,25 @@ impl Crud<PostForm> for Post {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
|
||||||
|
#[belongs_to(Post)]
|
||||||
|
#[table_name = "post_like"]
|
||||||
|
pub struct PostLike {
|
||||||
|
pub id: i32,
|
||||||
|
pub post_id: i32,
|
||||||
|
pub user_id: i32,
|
||||||
|
pub score: i16,
|
||||||
|
pub published: chrono::NaiveDateTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Insertable, AsChangeset, Clone)]
|
||||||
|
#[table_name="post_like"]
|
||||||
|
pub struct PostLikeForm {
|
||||||
|
pub post_id: i32,
|
||||||
|
pub user_id: i32,
|
||||||
|
pub score: i16
|
||||||
|
}
|
||||||
|
|
||||||
impl Likeable <PostLikeForm> for PostLike {
|
impl Likeable <PostLikeForm> for PostLike {
|
||||||
fn read(conn: &PgConnection, post_id_from: i32) -> Result<Vec<Self>, Error> {
|
fn read(conn: &PgConnection, post_id_from: i32) -> Result<Vec<Self>, Error> {
|
||||||
use schema::post_like::dsl::*;
|
use schema::post_like::dsl::*;
|
||||||
|
@ -102,6 +102,72 @@ impl Likeable <PostLikeForm> for PostLike {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
|
||||||
|
#[belongs_to(Post)]
|
||||||
|
#[table_name = "post_saved"]
|
||||||
|
pub struct PostSaved {
|
||||||
|
pub id: i32,
|
||||||
|
pub post_id: i32,
|
||||||
|
pub user_id: i32,
|
||||||
|
pub published: chrono::NaiveDateTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Insertable, AsChangeset, Clone)]
|
||||||
|
#[table_name="post_saved"]
|
||||||
|
pub struct PostSavedForm {
|
||||||
|
pub post_id: i32,
|
||||||
|
pub user_id: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Saveable <PostSavedForm> for PostSaved {
|
||||||
|
fn save(conn: &PgConnection, post_saved_form: &PostSavedForm) -> Result<Self, Error> {
|
||||||
|
use schema::post_saved::dsl::*;
|
||||||
|
insert_into(post_saved)
|
||||||
|
.values(post_saved_form)
|
||||||
|
.get_result::<Self>(conn)
|
||||||
|
}
|
||||||
|
fn unsave(conn: &PgConnection, post_saved_form: &PostSavedForm) -> Result<usize, Error> {
|
||||||
|
use schema::post_saved::dsl::*;
|
||||||
|
diesel::delete(post_saved
|
||||||
|
.filter(post_id.eq(post_saved_form.post_id))
|
||||||
|
.filter(user_id.eq(post_saved_form.user_id)))
|
||||||
|
.execute(conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
|
||||||
|
#[belongs_to(Post)]
|
||||||
|
#[table_name = "post_read"]
|
||||||
|
pub struct PostRead {
|
||||||
|
pub id: i32,
|
||||||
|
pub post_id: i32,
|
||||||
|
pub user_id: i32,
|
||||||
|
pub published: chrono::NaiveDateTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Insertable, AsChangeset, Clone)]
|
||||||
|
#[table_name="post_read"]
|
||||||
|
pub struct PostReadForm {
|
||||||
|
pub post_id: i32,
|
||||||
|
pub user_id: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Readable <PostReadForm> for PostRead {
|
||||||
|
fn mark_as_read(conn: &PgConnection, post_read_form: &PostReadForm) -> Result<Self, Error> {
|
||||||
|
use schema::post_read::dsl::*;
|
||||||
|
insert_into(post_read)
|
||||||
|
.values(post_read_form)
|
||||||
|
.get_result::<Self>(conn)
|
||||||
|
}
|
||||||
|
fn mark_as_unread(conn: &PgConnection, post_read_form: &PostReadForm) -> Result<usize, Error> {
|
||||||
|
use schema::post_read::dsl::*;
|
||||||
|
diesel::delete(post_read
|
||||||
|
.filter(post_id.eq(post_read_form.post_id))
|
||||||
|
.filter(user_id.eq(post_read_form.user_id)))
|
||||||
|
.execute(conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use establish_connection;
|
use establish_connection;
|
||||||
|
@ -132,7 +198,7 @@ mod tests {
|
||||||
description: None,
|
description: None,
|
||||||
category_id: 1,
|
category_id: 1,
|
||||||
creator_id: inserted_user.id,
|
creator_id: inserted_user.id,
|
||||||
removed: None,
|
removed: false,
|
||||||
updated: None
|
updated: None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -144,8 +210,8 @@ mod tests {
|
||||||
body: None,
|
body: None,
|
||||||
creator_id: inserted_user.id,
|
creator_id: inserted_user.id,
|
||||||
community_id: inserted_community.id,
|
community_id: inserted_community.id,
|
||||||
removed: None,
|
removed: false,
|
||||||
locked: None,
|
locked: false,
|
||||||
updated: None
|
updated: None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -159,11 +225,12 @@ mod tests {
|
||||||
creator_id: inserted_user.id,
|
creator_id: inserted_user.id,
|
||||||
community_id: inserted_community.id,
|
community_id: inserted_community.id,
|
||||||
published: inserted_post.published,
|
published: inserted_post.published,
|
||||||
removed: Some(false),
|
removed: false,
|
||||||
locked: Some(false),
|
locked: false,
|
||||||
updated: None
|
updated: None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Post Like
|
||||||
let post_like_form = PostLikeForm {
|
let post_like_form = PostLikeForm {
|
||||||
post_id: inserted_post.id,
|
post_id: inserted_post.id,
|
||||||
user_id: inserted_user.id,
|
user_id: inserted_user.id,
|
||||||
|
@ -180,9 +247,41 @@ mod tests {
|
||||||
score: 1
|
score: 1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Post Save
|
||||||
|
let post_saved_form = PostSavedForm {
|
||||||
|
post_id: inserted_post.id,
|
||||||
|
user_id: inserted_user.id,
|
||||||
|
};
|
||||||
|
|
||||||
|
let inserted_post_saved = PostSaved::save(&conn, &post_saved_form).unwrap();
|
||||||
|
|
||||||
|
let expected_post_saved = PostSaved {
|
||||||
|
id: inserted_post_saved.id,
|
||||||
|
post_id: inserted_post.id,
|
||||||
|
user_id: inserted_user.id,
|
||||||
|
published: inserted_post_saved.published,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Post Read
|
||||||
|
let post_read_form = PostReadForm {
|
||||||
|
post_id: inserted_post.id,
|
||||||
|
user_id: inserted_user.id,
|
||||||
|
};
|
||||||
|
|
||||||
|
let inserted_post_read = PostRead::mark_as_read(&conn, &post_read_form).unwrap();
|
||||||
|
|
||||||
|
let expected_post_read = PostRead {
|
||||||
|
id: inserted_post_read.id,
|
||||||
|
post_id: inserted_post.id,
|
||||||
|
user_id: inserted_user.id,
|
||||||
|
published: inserted_post_read.published,
|
||||||
|
};
|
||||||
|
|
||||||
let read_post = Post::read(&conn, inserted_post.id).unwrap();
|
let read_post = Post::read(&conn, inserted_post.id).unwrap();
|
||||||
let updated_post = Post::update(&conn, inserted_post.id, &new_post).unwrap();
|
let updated_post = Post::update(&conn, inserted_post.id, &new_post).unwrap();
|
||||||
let like_removed = PostLike::remove(&conn, &post_like_form).unwrap();
|
let like_removed = PostLike::remove(&conn, &post_like_form).unwrap();
|
||||||
|
let saved_removed = PostSaved::unsave(&conn, &post_saved_form).unwrap();
|
||||||
|
let read_removed = PostRead::mark_as_unread(&conn, &post_read_form).unwrap();
|
||||||
let num_deleted = Post::delete(&conn, inserted_post.id).unwrap();
|
let num_deleted = Post::delete(&conn, inserted_post.id).unwrap();
|
||||||
Community::delete(&conn, inserted_community.id).unwrap();
|
Community::delete(&conn, inserted_community.id).unwrap();
|
||||||
User_::delete(&conn, inserted_user.id).unwrap();
|
User_::delete(&conn, inserted_user.id).unwrap();
|
||||||
|
@ -191,7 +290,11 @@ mod tests {
|
||||||
assert_eq!(expected_post, inserted_post);
|
assert_eq!(expected_post, inserted_post);
|
||||||
assert_eq!(expected_post, updated_post);
|
assert_eq!(expected_post, updated_post);
|
||||||
assert_eq!(expected_post_like, inserted_post_like);
|
assert_eq!(expected_post_like, inserted_post_like);
|
||||||
|
assert_eq!(expected_post_saved, inserted_post_saved);
|
||||||
|
assert_eq!(expected_post_read, inserted_post_read);
|
||||||
assert_eq!(1, like_removed);
|
assert_eq!(1, like_removed);
|
||||||
|
assert_eq!(1, saved_removed);
|
||||||
|
assert_eq!(1, read_removed);
|
||||||
assert_eq!(1, num_deleted);
|
assert_eq!(1, num_deleted);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,8 @@ table! {
|
||||||
body -> Nullable<Text>,
|
body -> Nullable<Text>,
|
||||||
creator_id -> Int4,
|
creator_id -> Int4,
|
||||||
community_id -> Int4,
|
community_id -> Int4,
|
||||||
removed -> Nullable<Bool>,
|
removed -> Bool,
|
||||||
locked -> Nullable<Bool>,
|
locked -> Bool,
|
||||||
published -> Timestamp,
|
published -> Timestamp,
|
||||||
updated -> Nullable<Timestamp>,
|
updated -> Nullable<Timestamp>,
|
||||||
creator_name -> Varchar,
|
creator_name -> Varchar,
|
||||||
|
@ -33,7 +33,8 @@ table! {
|
||||||
user_id -> Nullable<Int4>,
|
user_id -> Nullable<Int4>,
|
||||||
my_vote -> Nullable<Int4>,
|
my_vote -> Nullable<Int4>,
|
||||||
subscribed -> Nullable<Bool>,
|
subscribed -> Nullable<Bool>,
|
||||||
am_mod -> Nullable<Bool>,
|
read -> Nullable<Bool>,
|
||||||
|
saved -> Nullable<Bool>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,8 +48,8 @@ pub struct PostView {
|
||||||
pub body: Option<String>,
|
pub body: Option<String>,
|
||||||
pub creator_id: i32,
|
pub creator_id: i32,
|
||||||
pub community_id: i32,
|
pub community_id: i32,
|
||||||
pub removed: Option<bool>,
|
pub removed: bool,
|
||||||
pub locked: Option<bool>,
|
pub locked: bool,
|
||||||
pub published: chrono::NaiveDateTime,
|
pub published: chrono::NaiveDateTime,
|
||||||
pub updated: Option<chrono::NaiveDateTime>,
|
pub updated: Option<chrono::NaiveDateTime>,
|
||||||
pub creator_name: String,
|
pub creator_name: String,
|
||||||
|
@ -61,7 +62,8 @@ pub struct PostView {
|
||||||
pub user_id: Option<i32>,
|
pub user_id: Option<i32>,
|
||||||
pub my_vote: Option<i32>,
|
pub my_vote: Option<i32>,
|
||||||
pub subscribed: Option<bool>,
|
pub subscribed: Option<bool>,
|
||||||
pub am_mod: Option<bool>,
|
pub read: Option<bool>,
|
||||||
|
pub saved: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PostView {
|
impl PostView {
|
||||||
|
@ -71,6 +73,8 @@ impl PostView {
|
||||||
for_community_id: Option<i32>,
|
for_community_id: Option<i32>,
|
||||||
for_creator_id: Option<i32>,
|
for_creator_id: Option<i32>,
|
||||||
my_user_id: Option<i32>,
|
my_user_id: Option<i32>,
|
||||||
|
saved_only: bool,
|
||||||
|
unread_only: bool,
|
||||||
page: Option<i64>,
|
page: Option<i64>,
|
||||||
limit: Option<i64>,
|
limit: Option<i64>,
|
||||||
) -> Result<Vec<Self>, Error> {
|
) -> Result<Vec<Self>, Error> {
|
||||||
|
@ -88,6 +92,15 @@ impl PostView {
|
||||||
query = query.filter(creator_id.eq(for_creator_id));
|
query = query.filter(creator_id.eq(for_creator_id));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO these are wrong, bc they'll only show saved for your logged in user, not theirs
|
||||||
|
if saved_only {
|
||||||
|
query = query.filter(saved.eq(true));
|
||||||
|
};
|
||||||
|
|
||||||
|
if unread_only {
|
||||||
|
query = query.filter(read.eq(false));
|
||||||
|
};
|
||||||
|
|
||||||
match type_ {
|
match type_ {
|
||||||
PostListingType::Subscribed => {
|
PostListingType::Subscribed => {
|
||||||
query = query.filter(subscribed.eq(true));
|
query = query.filter(subscribed.eq(true));
|
||||||
|
@ -187,7 +200,7 @@ mod tests {
|
||||||
description: None,
|
description: None,
|
||||||
creator_id: inserted_user.id,
|
creator_id: inserted_user.id,
|
||||||
category_id: 1,
|
category_id: 1,
|
||||||
removed: None,
|
removed: false,
|
||||||
updated: None
|
updated: None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -199,8 +212,8 @@ mod tests {
|
||||||
body: None,
|
body: None,
|
||||||
creator_id: inserted_user.id,
|
creator_id: inserted_user.id,
|
||||||
community_id: inserted_community.id,
|
community_id: inserted_community.id,
|
||||||
removed: None,
|
removed: false,
|
||||||
locked: None,
|
locked: false,
|
||||||
updated: None
|
updated: None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -239,8 +252,8 @@ mod tests {
|
||||||
creator_id: inserted_user.id,
|
creator_id: inserted_user.id,
|
||||||
creator_name: user_name.to_owned(),
|
creator_name: user_name.to_owned(),
|
||||||
community_id: inserted_community.id,
|
community_id: inserted_community.id,
|
||||||
removed: Some(false),
|
removed: false,
|
||||||
locked: Some(false),
|
locked: false,
|
||||||
community_name: community_name.to_owned(),
|
community_name: community_name.to_owned(),
|
||||||
number_of_comments: 0,
|
number_of_comments: 0,
|
||||||
score: 1,
|
score: 1,
|
||||||
|
@ -250,7 +263,8 @@ mod tests {
|
||||||
published: inserted_post.published,
|
published: inserted_post.published,
|
||||||
updated: None,
|
updated: None,
|
||||||
subscribed: None,
|
subscribed: None,
|
||||||
am_mod: None,
|
read: None,
|
||||||
|
saved: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let expected_post_listing_with_user = PostView {
|
let expected_post_listing_with_user = PostView {
|
||||||
|
@ -260,8 +274,8 @@ mod tests {
|
||||||
name: post_name.to_owned(),
|
name: post_name.to_owned(),
|
||||||
url: None,
|
url: None,
|
||||||
body: None,
|
body: None,
|
||||||
removed: Some(false),
|
removed: false,
|
||||||
locked: Some(false),
|
locked: false,
|
||||||
creator_id: inserted_user.id,
|
creator_id: inserted_user.id,
|
||||||
creator_name: user_name.to_owned(),
|
creator_name: user_name.to_owned(),
|
||||||
community_id: inserted_community.id,
|
community_id: inserted_community.id,
|
||||||
|
@ -274,12 +288,13 @@ mod tests {
|
||||||
published: inserted_post.published,
|
published: inserted_post.published,
|
||||||
updated: None,
|
updated: None,
|
||||||
subscribed: None,
|
subscribed: None,
|
||||||
am_mod: None,
|
read: None,
|
||||||
|
saved: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
let read_post_listings_with_user = PostView::list(&conn, PostListingType::Community, &SortType::New, Some(inserted_community.id), None, Some(inserted_user.id), None, None).unwrap();
|
let read_post_listings_with_user = PostView::list(&conn, PostListingType::Community, &SortType::New, Some(inserted_community.id), None, Some(inserted_user.id), false, false, None, None).unwrap();
|
||||||
let read_post_listings_no_user = PostView::list(&conn, PostListingType::Community, &SortType::New, Some(inserted_community.id), None, None, None, None).unwrap();
|
let read_post_listings_no_user = PostView::list(&conn, PostListingType::Community, &SortType::New, Some(inserted_community.id), None, None, false, false, None, 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();
|
||||||
let read_post_listing_with_user = PostView::read(&conn, inserted_post.id, Some(inserted_user.id)).unwrap();
|
let read_post_listing_with_user = PostView::read(&conn, inserted_post.id, Some(inserted_user.id)).unwrap();
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,16 @@ pub trait Bannable<T> {
|
||||||
fn unban(conn: &PgConnection, form: &T) -> Result<usize, Error> where Self: Sized;
|
fn unban(conn: &PgConnection, form: &T) -> Result<usize, Error> where Self: Sized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait Saveable<T> {
|
||||||
|
fn save(conn: &PgConnection, form: &T) -> Result<Self, Error> where Self: Sized;
|
||||||
|
fn unsave(conn: &PgConnection, form: &T) -> Result<usize, Error> where Self: Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Readable<T> {
|
||||||
|
fn mark_as_read(conn: &PgConnection, form: &T) -> Result<Self, Error> where Self: Sized;
|
||||||
|
fn mark_as_unread(conn: &PgConnection, form: &T) -> Result<usize, Error> where Self: Sized;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn establish_connection() -> PgConnection {
|
pub fn establish_connection() -> PgConnection {
|
||||||
let db_url = Settings::get().db_url;
|
let db_url = Settings::get().db_url;
|
||||||
PgConnection::establish(&db_url)
|
PgConnection::establish(&db_url)
|
||||||
|
|
|
@ -12,7 +12,8 @@ table! {
|
||||||
post_id -> Int4,
|
post_id -> Int4,
|
||||||
parent_id -> Nullable<Int4>,
|
parent_id -> Nullable<Int4>,
|
||||||
content -> Text,
|
content -> Text,
|
||||||
removed -> Nullable<Bool>,
|
removed -> Bool,
|
||||||
|
read -> Bool,
|
||||||
published -> Timestamp,
|
published -> Timestamp,
|
||||||
updated -> Nullable<Timestamp>,
|
updated -> Nullable<Timestamp>,
|
||||||
}
|
}
|
||||||
|
@ -29,6 +30,15 @@ table! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table! {
|
||||||
|
comment_saved (id) {
|
||||||
|
id -> Int4,
|
||||||
|
comment_id -> Int4,
|
||||||
|
user_id -> Int4,
|
||||||
|
published -> Timestamp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
table! {
|
table! {
|
||||||
community (id) {
|
community (id) {
|
||||||
id -> Int4,
|
id -> Int4,
|
||||||
|
@ -37,7 +47,7 @@ table! {
|
||||||
description -> Nullable<Text>,
|
description -> Nullable<Text>,
|
||||||
category_id -> Int4,
|
category_id -> Int4,
|
||||||
creator_id -> Int4,
|
creator_id -> Int4,
|
||||||
removed -> Nullable<Bool>,
|
removed -> Bool,
|
||||||
published -> Timestamp,
|
published -> Timestamp,
|
||||||
updated -> Nullable<Timestamp>,
|
updated -> Nullable<Timestamp>,
|
||||||
}
|
}
|
||||||
|
@ -168,8 +178,8 @@ table! {
|
||||||
body -> Nullable<Text>,
|
body -> Nullable<Text>,
|
||||||
creator_id -> Int4,
|
creator_id -> Int4,
|
||||||
community_id -> Int4,
|
community_id -> Int4,
|
||||||
removed -> Nullable<Bool>,
|
removed -> Bool,
|
||||||
locked -> Nullable<Bool>,
|
locked -> Bool,
|
||||||
published -> Timestamp,
|
published -> Timestamp,
|
||||||
updated -> Nullable<Timestamp>,
|
updated -> Nullable<Timestamp>,
|
||||||
}
|
}
|
||||||
|
@ -185,6 +195,24 @@ table! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table! {
|
||||||
|
post_read (id) {
|
||||||
|
id -> Int4,
|
||||||
|
post_id -> Int4,
|
||||||
|
user_id -> Int4,
|
||||||
|
published -> Timestamp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table! {
|
||||||
|
post_saved (id) {
|
||||||
|
id -> Int4,
|
||||||
|
post_id -> Int4,
|
||||||
|
user_id -> Int4,
|
||||||
|
published -> Timestamp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
table! {
|
table! {
|
||||||
site (id) {
|
site (id) {
|
||||||
id -> Int4,
|
id -> Int4,
|
||||||
|
@ -225,6 +253,8 @@ joinable!(comment -> user_ (creator_id));
|
||||||
joinable!(comment_like -> comment (comment_id));
|
joinable!(comment_like -> comment (comment_id));
|
||||||
joinable!(comment_like -> post (post_id));
|
joinable!(comment_like -> post (post_id));
|
||||||
joinable!(comment_like -> user_ (user_id));
|
joinable!(comment_like -> user_ (user_id));
|
||||||
|
joinable!(comment_saved -> comment (comment_id));
|
||||||
|
joinable!(comment_saved -> user_ (user_id));
|
||||||
joinable!(community -> category (category_id));
|
joinable!(community -> category (category_id));
|
||||||
joinable!(community -> user_ (creator_id));
|
joinable!(community -> user_ (creator_id));
|
||||||
joinable!(community_follower -> community (community_id));
|
joinable!(community_follower -> community (community_id));
|
||||||
|
@ -247,6 +277,10 @@ joinable!(post -> community (community_id));
|
||||||
joinable!(post -> user_ (creator_id));
|
joinable!(post -> user_ (creator_id));
|
||||||
joinable!(post_like -> post (post_id));
|
joinable!(post_like -> post (post_id));
|
||||||
joinable!(post_like -> user_ (user_id));
|
joinable!(post_like -> user_ (user_id));
|
||||||
|
joinable!(post_read -> post (post_id));
|
||||||
|
joinable!(post_read -> user_ (user_id));
|
||||||
|
joinable!(post_saved -> post (post_id));
|
||||||
|
joinable!(post_saved -> user_ (user_id));
|
||||||
joinable!(site -> user_ (creator_id));
|
joinable!(site -> user_ (creator_id));
|
||||||
joinable!(user_ban -> user_ (user_id));
|
joinable!(user_ban -> user_ (user_id));
|
||||||
|
|
||||||
|
@ -254,6 +288,7 @@ allow_tables_to_appear_in_same_query!(
|
||||||
category,
|
category,
|
||||||
comment,
|
comment,
|
||||||
comment_like,
|
comment_like,
|
||||||
|
comment_saved,
|
||||||
community,
|
community,
|
||||||
community_follower,
|
community_follower,
|
||||||
community_moderator,
|
community_moderator,
|
||||||
|
@ -268,6 +303,8 @@ allow_tables_to_appear_in_same_query!(
|
||||||
mod_remove_post,
|
mod_remove_post,
|
||||||
post,
|
post,
|
||||||
post_like,
|
post_like,
|
||||||
|
post_read,
|
||||||
|
post_saved,
|
||||||
site,
|
site,
|
||||||
user_,
|
user_,
|
||||||
user_ban,
|
user_ban,
|
||||||
|
|
|
@ -11,7 +11,7 @@ use bcrypt::{verify};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use diesel::PgConnection;
|
use diesel::PgConnection;
|
||||||
|
|
||||||
use {Crud, Joinable, Likeable, Followable, Bannable, establish_connection, naive_now, naive_from_unix, SortType, has_slurs, remove_slurs};
|
use {Crud, Joinable, Likeable, Followable, Bannable, Saveable, establish_connection, naive_now, naive_from_unix, SortType, has_slurs, remove_slurs};
|
||||||
use actions::community::*;
|
use actions::community::*;
|
||||||
use actions::user::*;
|
use actions::user::*;
|
||||||
use actions::post::*;
|
use actions::post::*;
|
||||||
|
@ -26,7 +26,7 @@ use actions::moderator::*;
|
||||||
|
|
||||||
#[derive(EnumString,ToString,Debug)]
|
#[derive(EnumString,ToString,Debug)]
|
||||||
pub enum UserOperation {
|
pub enum UserOperation {
|
||||||
Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, EditCommunity, FollowCommunity, GetFollowedCommunities, GetUserDetails, GetModlog, BanFromCommunity, AddModToCommunity, CreateSite, EditSite, GetSite, AddAdmin, BanUser
|
Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, SaveComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, SavePost, EditCommunity, FollowCommunity, GetFollowedCommunities, GetUserDetails, GetModlog, BanFromCommunity, AddModToCommunity, CreateSite, EditSite, GetSite, AddAdmin, BanUser
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
|
@ -164,7 +164,8 @@ pub struct GetPostResponse {
|
||||||
post: PostView,
|
post: PostView,
|
||||||
comments: Vec<CommentView>,
|
comments: Vec<CommentView>,
|
||||||
community: CommunityView,
|
community: CommunityView,
|
||||||
moderators: Vec<CommunityModeratorView>
|
moderators: Vec<CommunityModeratorView>,
|
||||||
|
admins: Vec<UserView>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
|
@ -217,6 +218,13 @@ pub struct EditComment {
|
||||||
auth: String
|
auth: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct SaveComment {
|
||||||
|
comment_id: i32,
|
||||||
|
save: bool,
|
||||||
|
auth: String
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct CommentResponse {
|
pub struct CommentResponse {
|
||||||
op: String,
|
op: String,
|
||||||
|
@ -253,9 +261,16 @@ pub struct EditPost {
|
||||||
name: String,
|
name: String,
|
||||||
url: Option<String>,
|
url: Option<String>,
|
||||||
body: Option<String>,
|
body: Option<String>,
|
||||||
removed: Option<bool>,
|
removed: bool,
|
||||||
|
locked: bool,
|
||||||
reason: Option<String>,
|
reason: Option<String>,
|
||||||
locked: Option<bool>,
|
auth: String
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct SavePost {
|
||||||
|
post_id: i32,
|
||||||
|
save: bool,
|
||||||
auth: String
|
auth: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,7 +281,7 @@ pub struct EditCommunity {
|
||||||
title: String,
|
title: String,
|
||||||
description: Option<String>,
|
description: Option<String>,
|
||||||
category_id: i32,
|
category_id: i32,
|
||||||
removed: Option<bool>,
|
removed: bool,
|
||||||
reason: Option<String>,
|
reason: Option<String>,
|
||||||
expires: Option<i64>,
|
expires: Option<i64>,
|
||||||
auth: String
|
auth: String
|
||||||
|
@ -297,7 +312,7 @@ pub struct GetUserDetails {
|
||||||
page: Option<i64>,
|
page: Option<i64>,
|
||||||
limit: Option<i64>,
|
limit: Option<i64>,
|
||||||
community_id: Option<i32>,
|
community_id: Option<i32>,
|
||||||
auth: Option<String>
|
saved_only: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
|
@ -308,8 +323,6 @@ pub struct GetUserDetailsResponse {
|
||||||
moderates: Vec<CommunityModeratorView>,
|
moderates: Vec<CommunityModeratorView>,
|
||||||
comments: Vec<CommentView>,
|
comments: Vec<CommentView>,
|
||||||
posts: Vec<PostView>,
|
posts: Vec<PostView>,
|
||||||
saved_posts: Vec<PostView>,
|
|
||||||
saved_comments: Vec<CommentView>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
|
@ -468,6 +481,8 @@ impl ChatServer {
|
||||||
Some(community_id),
|
Some(community_id),
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
None,
|
None,
|
||||||
Some(9999))
|
Some(9999))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -491,7 +506,6 @@ impl Handler<Connect> for ChatServer {
|
||||||
type Result = usize;
|
type Result = usize;
|
||||||
|
|
||||||
fn handle(&mut self, msg: Connect, _: &mut Context<Self>) -> Self::Result {
|
fn handle(&mut self, msg: Connect, _: &mut Context<Self>) -> Self::Result {
|
||||||
println!("Someone joined");
|
|
||||||
|
|
||||||
// notify all users in same room
|
// notify all users in same room
|
||||||
// self.send_room_message(&"Main".to_owned(), "Someone joined", 0);
|
// self.send_room_message(&"Main".to_owned(), "Someone joined", 0);
|
||||||
|
@ -513,7 +527,6 @@ impl Handler<Disconnect> for ChatServer {
|
||||||
type Result = ();
|
type Result = ();
|
||||||
|
|
||||||
fn handle(&mut self, msg: Disconnect, _: &mut Context<Self>) {
|
fn handle(&mut self, msg: Disconnect, _: &mut Context<Self>) {
|
||||||
println!("Someone disconnected");
|
|
||||||
|
|
||||||
// let mut rooms: Vec<i32> = Vec::new();
|
// let mut rooms: Vec<i32> = Vec::new();
|
||||||
|
|
||||||
|
@ -586,6 +599,10 @@ impl Handler<StandardMessage> for ChatServer {
|
||||||
let edit_comment: EditComment = serde_json::from_str(data).unwrap();
|
let edit_comment: EditComment = serde_json::from_str(data).unwrap();
|
||||||
edit_comment.perform(self, msg.id)
|
edit_comment.perform(self, msg.id)
|
||||||
},
|
},
|
||||||
|
UserOperation::SaveComment => {
|
||||||
|
let save_post: SaveComment = serde_json::from_str(data).unwrap();
|
||||||
|
save_post.perform(self, msg.id)
|
||||||
|
},
|
||||||
UserOperation::CreateCommentLike => {
|
UserOperation::CreateCommentLike => {
|
||||||
let create_comment_like: CreateCommentLike = serde_json::from_str(data).unwrap();
|
let create_comment_like: CreateCommentLike = serde_json::from_str(data).unwrap();
|
||||||
create_comment_like.perform(self, msg.id)
|
create_comment_like.perform(self, msg.id)
|
||||||
|
@ -602,6 +619,10 @@ impl Handler<StandardMessage> for ChatServer {
|
||||||
let edit_post: EditPost = serde_json::from_str(data).unwrap();
|
let edit_post: EditPost = serde_json::from_str(data).unwrap();
|
||||||
edit_post.perform(self, msg.id)
|
edit_post.perform(self, msg.id)
|
||||||
},
|
},
|
||||||
|
UserOperation::SavePost => {
|
||||||
|
let save_post: SavePost = serde_json::from_str(data).unwrap();
|
||||||
|
save_post.perform(self, msg.id)
|
||||||
|
},
|
||||||
UserOperation::EditCommunity => {
|
UserOperation::EditCommunity => {
|
||||||
let edit_community: EditCommunity = serde_json::from_str(data).unwrap();
|
let edit_community: EditCommunity = serde_json::from_str(data).unwrap();
|
||||||
edit_community.perform(self, msg.id)
|
edit_community.perform(self, msg.id)
|
||||||
|
@ -745,11 +766,11 @@ impl Perform for Register {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// If its an admin, add them as a mod to main
|
// If its an admin, add them as a mod and follower to main
|
||||||
if self.admin {
|
if self.admin {
|
||||||
let community_moderator_form = CommunityModeratorForm {
|
let community_moderator_form = CommunityModeratorForm {
|
||||||
community_id: 1,
|
community_id: 1,
|
||||||
user_id: inserted_user.id
|
user_id: inserted_user.id,
|
||||||
};
|
};
|
||||||
|
|
||||||
let _inserted_community_moderator = match CommunityModerator::join(&conn, &community_moderator_form) {
|
let _inserted_community_moderator = match CommunityModerator::join(&conn, &community_moderator_form) {
|
||||||
|
@ -758,6 +779,18 @@ impl Perform for Register {
|
||||||
return self.error("Community moderator already exists.");
|
return self.error("Community moderator already exists.");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let community_follower_form = CommunityFollowerForm {
|
||||||
|
community_id: 1,
|
||||||
|
user_id: inserted_user.id,
|
||||||
|
};
|
||||||
|
|
||||||
|
let _inserted_community_follower = match CommunityFollower::follow(&conn, &community_follower_form) {
|
||||||
|
Ok(user) => user,
|
||||||
|
Err(_e) => {
|
||||||
|
return self.error("Community follower already exists.");
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -797,6 +830,11 @@ impl Perform for CreateCommunity {
|
||||||
|
|
||||||
let user_id = claims.id;
|
let user_id = claims.id;
|
||||||
|
|
||||||
|
// Check for a site ban
|
||||||
|
if UserView::read(&conn, user_id).unwrap().banned {
|
||||||
|
return self.error("You have been banned from the site");
|
||||||
|
}
|
||||||
|
|
||||||
// When you create a community, make sure the user becomes a moderator and a follower
|
// When you create a community, make sure the user becomes a moderator and a follower
|
||||||
|
|
||||||
let community_form = CommunityForm {
|
let community_form = CommunityForm {
|
||||||
|
@ -805,7 +843,7 @@ impl Perform for CreateCommunity {
|
||||||
description: self.description.to_owned(),
|
description: self.description.to_owned(),
|
||||||
category_id: self.category_id,
|
category_id: self.category_id,
|
||||||
creator_id: user_id,
|
creator_id: user_id,
|
||||||
removed: None,
|
removed: false,
|
||||||
updated: None,
|
updated: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -934,19 +972,24 @@ impl Perform for CreatePost {
|
||||||
|
|
||||||
let user_id = claims.id;
|
let user_id = claims.id;
|
||||||
|
|
||||||
// Check for a ban
|
// Check for a community ban
|
||||||
if CommunityUserBanView::get(&conn, user_id, self.community_id).is_ok() {
|
if CommunityUserBanView::get(&conn, user_id, self.community_id).is_ok() {
|
||||||
return self.error("You have been banned from this community");
|
return self.error("You have been banned from this community");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for a site ban
|
||||||
|
if UserView::read(&conn, user_id).unwrap().banned {
|
||||||
|
return self.error("You have been banned from the site");
|
||||||
|
}
|
||||||
|
|
||||||
let post_form = PostForm {
|
let post_form = PostForm {
|
||||||
name: self.name.to_owned(),
|
name: self.name.to_owned(),
|
||||||
url: self.url.to_owned(),
|
url: self.url.to_owned(),
|
||||||
body: self.body.to_owned(),
|
body: self.body.to_owned(),
|
||||||
community_id: self.community_id,
|
community_id: self.community_id,
|
||||||
creator_id: user_id,
|
creator_id: user_id,
|
||||||
removed: None,
|
removed: false,
|
||||||
locked: None,
|
locked: false,
|
||||||
updated: None
|
updated: None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1031,12 +1074,14 @@ impl Perform for GetPost {
|
||||||
|
|
||||||
chat.rooms.get_mut(&self.id).unwrap().insert(addr);
|
chat.rooms.get_mut(&self.id).unwrap().insert(addr);
|
||||||
|
|
||||||
let comments = CommentView::list(&conn, &SortType::New, Some(self.id), None, user_id, None, Some(9999)).unwrap();
|
let comments = CommentView::list(&conn, &SortType::New, Some(self.id), None, user_id, false, None, Some(9999)).unwrap();
|
||||||
|
|
||||||
let community = CommunityView::read(&conn, post_view.community_id, user_id).unwrap();
|
let community = CommunityView::read(&conn, post_view.community_id, user_id).unwrap();
|
||||||
|
|
||||||
let moderators = CommunityModeratorView::for_community(&conn, post_view.community_id).unwrap();
|
let moderators = CommunityModeratorView::for_community(&conn, post_view.community_id).unwrap();
|
||||||
|
|
||||||
|
let admins = UserView::admins(&conn).unwrap();
|
||||||
|
|
||||||
// Return the jwt
|
// Return the jwt
|
||||||
serde_json::to_string(
|
serde_json::to_string(
|
||||||
&GetPostResponse {
|
&GetPostResponse {
|
||||||
|
@ -1044,7 +1089,8 @@ impl Perform for GetPost {
|
||||||
post: post_view,
|
post: post_view,
|
||||||
comments: comments,
|
comments: comments,
|
||||||
community: community,
|
community: community,
|
||||||
moderators: moderators
|
moderators: moderators,
|
||||||
|
admins: admins,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -1117,12 +1163,17 @@ impl Perform for CreateComment {
|
||||||
|
|
||||||
let user_id = claims.id;
|
let user_id = claims.id;
|
||||||
|
|
||||||
// Check for a ban
|
// Check for a community ban
|
||||||
let post = Post::read(&conn, self.post_id).unwrap();
|
let post = Post::read(&conn, self.post_id).unwrap();
|
||||||
if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() {
|
if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() {
|
||||||
return self.error("You have been banned from this community");
|
return self.error("You have been banned from this community");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for a site ban
|
||||||
|
if UserView::read(&conn, user_id).unwrap().banned {
|
||||||
|
return self.error("You have been banned from the site");
|
||||||
|
}
|
||||||
|
|
||||||
let content_slurs_removed = remove_slurs(&self.content.to_owned());
|
let content_slurs_removed = remove_slurs(&self.content.to_owned());
|
||||||
|
|
||||||
let comment_form = CommentForm {
|
let comment_form = CommentForm {
|
||||||
|
@ -1202,24 +1253,38 @@ impl Perform for EditComment {
|
||||||
|
|
||||||
let user_id = claims.id;
|
let user_id = claims.id;
|
||||||
|
|
||||||
|
// Verify its the creator or a mod, or an admin
|
||||||
// Verify its the creator or a mod
|
|
||||||
let orig_comment = CommentView::read(&conn, self.edit_id, None).unwrap();
|
let orig_comment = CommentView::read(&conn, self.edit_id, None).unwrap();
|
||||||
let mut editors: Vec<i32> = CommunityModeratorView::for_community(&conn, orig_comment.community_id)
|
let mut editors: Vec<i32> = vec![self.creator_id];
|
||||||
|
editors.append(
|
||||||
|
&mut CommunityModeratorView::for_community(&conn, orig_comment.community_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|m| m.user_id)
|
.map(|m| m.user_id)
|
||||||
.collect();
|
.collect()
|
||||||
editors.push(self.creator_id);
|
);
|
||||||
|
editors.append(
|
||||||
|
&mut UserView::admins(&conn)
|
||||||
|
.unwrap()
|
||||||
|
.into_iter()
|
||||||
|
.map(|a| a.id)
|
||||||
|
.collect()
|
||||||
|
);
|
||||||
|
|
||||||
if !editors.contains(&user_id) {
|
if !editors.contains(&user_id) {
|
||||||
return self.error("Not allowed to edit comment.");
|
return self.error("Not allowed to edit comment.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for a ban
|
// Check for a community ban
|
||||||
if CommunityUserBanView::get(&conn, user_id, orig_comment.community_id).is_ok() {
|
if CommunityUserBanView::get(&conn, user_id, orig_comment.community_id).is_ok() {
|
||||||
return self.error("You have been banned from this community");
|
return self.error("You have been banned from this community");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for a site ban
|
||||||
|
if UserView::read(&conn, user_id).unwrap().banned {
|
||||||
|
return self.error("You have been banned from the site");
|
||||||
|
}
|
||||||
|
|
||||||
let content_slurs_removed = remove_slurs(&self.content.to_owned());
|
let content_slurs_removed = remove_slurs(&self.content.to_owned());
|
||||||
|
|
||||||
let comment_form = CommentForm {
|
let comment_form = CommentForm {
|
||||||
|
@ -1278,6 +1343,60 @@ impl Perform for EditComment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Perform for SaveComment {
|
||||||
|
fn op_type(&self) -> UserOperation {
|
||||||
|
UserOperation::SaveComment
|
||||||
|
}
|
||||||
|
|
||||||
|
fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> String {
|
||||||
|
|
||||||
|
let conn = establish_connection();
|
||||||
|
|
||||||
|
let claims = match Claims::decode(&self.auth) {
|
||||||
|
Ok(claims) => claims.claims,
|
||||||
|
Err(_e) => {
|
||||||
|
return self.error("Not logged in.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let user_id = claims.id;
|
||||||
|
|
||||||
|
let comment_saved_form = CommentSavedForm {
|
||||||
|
comment_id: self.comment_id,
|
||||||
|
user_id: user_id,
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.save {
|
||||||
|
match CommentSaved::save(&conn, &comment_saved_form) {
|
||||||
|
Ok(comment) => comment,
|
||||||
|
Err(_e) => {
|
||||||
|
return self.error("Couldnt do comment save");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
match CommentSaved::unsave(&conn, &comment_saved_form) {
|
||||||
|
Ok(comment) => comment,
|
||||||
|
Err(_e) => {
|
||||||
|
return self.error("Couldnt do comment save");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let comment_view = CommentView::read(&conn, self.comment_id, Some(user_id)).unwrap();
|
||||||
|
|
||||||
|
let comment_out = serde_json::to_string(
|
||||||
|
&CommentResponse {
|
||||||
|
op: self.op_type().to_string(),
|
||||||
|
comment: comment_view
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
comment_out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Perform for CreateCommentLike {
|
impl Perform for CreateCommentLike {
|
||||||
fn op_type(&self) -> UserOperation {
|
fn op_type(&self) -> UserOperation {
|
||||||
UserOperation::CreateCommentLike
|
UserOperation::CreateCommentLike
|
||||||
|
@ -1296,12 +1415,17 @@ impl Perform for CreateCommentLike {
|
||||||
|
|
||||||
let user_id = claims.id;
|
let user_id = claims.id;
|
||||||
|
|
||||||
// Check for a ban
|
// Check for a community ban
|
||||||
let post = Post::read(&conn, self.post_id).unwrap();
|
let post = Post::read(&conn, self.post_id).unwrap();
|
||||||
if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() {
|
if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() {
|
||||||
return self.error("You have been banned from this community");
|
return self.error("You have been banned from this community");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for a site ban
|
||||||
|
if UserView::read(&conn, user_id).unwrap().banned {
|
||||||
|
return self.error("You have been banned from the site");
|
||||||
|
}
|
||||||
|
|
||||||
let like_form = CommentLikeForm {
|
let like_form = CommentLikeForm {
|
||||||
comment_id: self.comment_id,
|
comment_id: self.comment_id,
|
||||||
post_id: self.post_id,
|
post_id: self.post_id,
|
||||||
|
@ -1377,7 +1501,7 @@ impl Perform for GetPosts {
|
||||||
let type_ = PostListingType::from_str(&self.type_).expect("listing type");
|
let type_ = PostListingType::from_str(&self.type_).expect("listing type");
|
||||||
let sort = SortType::from_str(&self.sort).expect("listing sort");
|
let sort = SortType::from_str(&self.sort).expect("listing sort");
|
||||||
|
|
||||||
let posts = match PostView::list(&conn, type_, &sort, self.community_id, None, user_id, self.page, self.limit) {
|
let posts = match PostView::list(&conn, type_, &sort, self.community_id, None, user_id, false, false, self.page, self.limit) {
|
||||||
Ok(posts) => posts,
|
Ok(posts) => posts,
|
||||||
Err(_e) => {
|
Err(_e) => {
|
||||||
return self.error("Couldn't get posts");
|
return self.error("Couldn't get posts");
|
||||||
|
@ -1414,12 +1538,17 @@ impl Perform for CreatePostLike {
|
||||||
|
|
||||||
let user_id = claims.id;
|
let user_id = claims.id;
|
||||||
|
|
||||||
// Check for a ban
|
// Check for a community ban
|
||||||
let post = Post::read(&conn, self.post_id).unwrap();
|
let post = Post::read(&conn, self.post_id).unwrap();
|
||||||
if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() {
|
if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() {
|
||||||
return self.error("You have been banned from this community");
|
return self.error("You have been banned from this community");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for a site ban
|
||||||
|
if UserView::read(&conn, user_id).unwrap().banned {
|
||||||
|
return self.error("You have been banned from the site");
|
||||||
|
}
|
||||||
|
|
||||||
let like_form = PostLikeForm {
|
let like_form = PostLikeForm {
|
||||||
post_id: self.post_id,
|
post_id: self.post_id,
|
||||||
user_id: user_id,
|
user_id: user_id,
|
||||||
|
@ -1494,11 +1623,16 @@ impl Perform for EditPost {
|
||||||
return self.error("Not allowed to edit comment.");
|
return self.error("Not allowed to edit comment.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for a ban
|
// Check for a community ban
|
||||||
if CommunityUserBanView::get(&conn, user_id, self.community_id).is_ok() {
|
if CommunityUserBanView::get(&conn, user_id, self.community_id).is_ok() {
|
||||||
return self.error("You have been banned from this community");
|
return self.error("You have been banned from this community");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for a site ban
|
||||||
|
if UserView::read(&conn, user_id).unwrap().banned {
|
||||||
|
return self.error("You have been banned from the site");
|
||||||
|
}
|
||||||
|
|
||||||
let post_form = PostForm {
|
let post_form = PostForm {
|
||||||
name: self.name.to_owned(),
|
name: self.name.to_owned(),
|
||||||
url: self.url.to_owned(),
|
url: self.url.to_owned(),
|
||||||
|
@ -1518,21 +1652,21 @@ impl Perform for EditPost {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Mod tables
|
// Mod tables
|
||||||
if let Some(removed) = self.removed.to_owned() {
|
if self.removed {
|
||||||
let form = ModRemovePostForm {
|
let form = ModRemovePostForm {
|
||||||
mod_user_id: user_id,
|
mod_user_id: user_id,
|
||||||
post_id: self.edit_id,
|
post_id: self.edit_id,
|
||||||
removed: Some(removed),
|
removed: Some(self.removed),
|
||||||
reason: self.reason.to_owned(),
|
reason: self.reason.to_owned(),
|
||||||
};
|
};
|
||||||
ModRemovePost::create(&conn, &form).unwrap();
|
ModRemovePost::create(&conn, &form).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(locked) = self.locked.to_owned() {
|
if self.locked {
|
||||||
let form = ModLockPostForm {
|
let form = ModLockPostForm {
|
||||||
mod_user_id: user_id,
|
mod_user_id: user_id,
|
||||||
post_id: self.edit_id,
|
post_id: self.edit_id,
|
||||||
locked: Some(locked),
|
locked: Some(self.locked),
|
||||||
};
|
};
|
||||||
ModLockPost::create(&conn, &form).unwrap();
|
ModLockPost::create(&conn, &form).unwrap();
|
||||||
}
|
}
|
||||||
|
@ -1564,6 +1698,59 @@ impl Perform for EditPost {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Perform for SavePost {
|
||||||
|
fn op_type(&self) -> UserOperation {
|
||||||
|
UserOperation::SavePost
|
||||||
|
}
|
||||||
|
|
||||||
|
fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> String {
|
||||||
|
|
||||||
|
let conn = establish_connection();
|
||||||
|
|
||||||
|
let claims = match Claims::decode(&self.auth) {
|
||||||
|
Ok(claims) => claims.claims,
|
||||||
|
Err(_e) => {
|
||||||
|
return self.error("Not logged in.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let user_id = claims.id;
|
||||||
|
|
||||||
|
let post_saved_form = PostSavedForm {
|
||||||
|
post_id: self.post_id,
|
||||||
|
user_id: user_id,
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.save {
|
||||||
|
match PostSaved::save(&conn, &post_saved_form) {
|
||||||
|
Ok(post) => post,
|
||||||
|
Err(_e) => {
|
||||||
|
return self.error("Couldnt do post save");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
match PostSaved::unsave(&conn, &post_saved_form) {
|
||||||
|
Ok(post) => post,
|
||||||
|
Err(_e) => {
|
||||||
|
return self.error("Couldnt do post save");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let post_view = PostView::read(&conn, self.post_id, Some(user_id)).unwrap();
|
||||||
|
|
||||||
|
let post_out = serde_json::to_string(
|
||||||
|
&PostResponse {
|
||||||
|
op: self.op_type().to_string(),
|
||||||
|
post: post_view
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
post_out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Perform for EditCommunity {
|
impl Perform for EditCommunity {
|
||||||
fn op_type(&self) -> UserOperation {
|
fn op_type(&self) -> UserOperation {
|
||||||
UserOperation::EditCommunity
|
UserOperation::EditCommunity
|
||||||
|
@ -1586,6 +1773,11 @@ impl Perform for EditCommunity {
|
||||||
|
|
||||||
let user_id = claims.id;
|
let user_id = claims.id;
|
||||||
|
|
||||||
|
// Check for a site ban
|
||||||
|
if UserView::read(&conn, user_id).unwrap().banned {
|
||||||
|
return self.error("You have been banned from the site");
|
||||||
|
}
|
||||||
|
|
||||||
// Verify its a mod
|
// Verify its a mod
|
||||||
let moderator_view = CommunityModeratorView::for_community(&conn, self.edit_id).unwrap();
|
let moderator_view = CommunityModeratorView::for_community(&conn, self.edit_id).unwrap();
|
||||||
let mod_ids: Vec<i32> = moderator_view.into_iter().map(|m| m.user_id).collect();
|
let mod_ids: Vec<i32> = moderator_view.into_iter().map(|m| m.user_id).collect();
|
||||||
|
@ -1611,7 +1803,7 @@ impl Perform for EditCommunity {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Mod tables
|
// Mod tables
|
||||||
if let Some(removed) = self.removed.to_owned() {
|
if self.removed {
|
||||||
let expires = match self.expires {
|
let expires = match self.expires {
|
||||||
Some(time) => Some(naive_from_unix(time)),
|
Some(time) => Some(naive_from_unix(time)),
|
||||||
None => None
|
None => None
|
||||||
|
@ -1619,7 +1811,7 @@ impl Perform for EditCommunity {
|
||||||
let form = ModRemoveCommunityForm {
|
let form = ModRemoveCommunityForm {
|
||||||
mod_user_id: user_id,
|
mod_user_id: user_id,
|
||||||
community_id: self.edit_id,
|
community_id: self.edit_id,
|
||||||
removed: Some(removed),
|
removed: Some(self.removed),
|
||||||
reason: self.reason.to_owned(),
|
reason: self.reason.to_owned(),
|
||||||
expires: expires
|
expires: expires
|
||||||
};
|
};
|
||||||
|
@ -1750,26 +1942,21 @@ impl Perform for GetUserDetails {
|
||||||
|
|
||||||
let conn = establish_connection();
|
let conn = establish_connection();
|
||||||
|
|
||||||
let user_id: Option<i32> = match &self.auth {
|
|
||||||
Some(auth) => {
|
|
||||||
match Claims::decode(&auth) {
|
|
||||||
Ok(claims) => {
|
|
||||||
let user_id = claims.claims.id;
|
|
||||||
Some(user_id)
|
|
||||||
}
|
|
||||||
Err(_e) => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => None
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
//TODO add save
|
//TODO add save
|
||||||
let sort = SortType::from_str(&self.sort).expect("listing sort");
|
let sort = SortType::from_str(&self.sort).expect("listing sort");
|
||||||
|
|
||||||
let user_view = UserView::read(&conn, self.user_id).unwrap();
|
let user_view = UserView::read(&conn, self.user_id).unwrap();
|
||||||
let posts = PostView::list(&conn, PostListingType::All, &sort, self.community_id, Some(self.user_id), user_id, self.page, self.limit).unwrap();
|
let posts = if self.saved_only {
|
||||||
let comments = CommentView::list(&conn, &sort, None, Some(self.user_id), user_id, self.page, self.limit).unwrap();
|
PostView::list(&conn, PostListingType::All, &sort, self.community_id, None, Some(self.user_id), self.saved_only, false, self.page, self.limit).unwrap()
|
||||||
|
} else {
|
||||||
|
PostView::list(&conn, PostListingType::All, &sort, self.community_id, Some(self.user_id), None, self.saved_only, false, self.page, self.limit).unwrap()
|
||||||
|
};
|
||||||
|
let comments = if self.saved_only {
|
||||||
|
CommentView::list(&conn, &sort, None, None, Some(self.user_id), self.saved_only, self.page, self.limit).unwrap()
|
||||||
|
} else {
|
||||||
|
CommentView::list(&conn, &sort, None, Some(self.user_id), None, self.saved_only, self.page, self.limit).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
let follows = CommunityFollowerView::for_user(&conn, self.user_id).unwrap();
|
let follows = CommunityFollowerView::for_user(&conn, self.user_id).unwrap();
|
||||||
let moderates = CommunityModeratorView::for_user(&conn, self.user_id).unwrap();
|
let moderates = CommunityModeratorView::for_user(&conn, self.user_id).unwrap();
|
||||||
|
|
||||||
|
@ -1782,8 +1969,6 @@ impl Perform for GetUserDetails {
|
||||||
moderates: moderates,
|
moderates: moderates,
|
||||||
comments: comments,
|
comments: comments,
|
||||||
posts: posts,
|
posts: posts,
|
||||||
saved_posts: Vec::new(),
|
|
||||||
saved_comments: Vec::new(),
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
import { Component, linkEvent } from 'inferno';
|
import { Component, linkEvent } from 'inferno';
|
||||||
import { Link } from 'inferno-router';
|
import { Link } from 'inferno-router';
|
||||||
import { CommentNode as CommentNodeI, CommentLikeForm, CommentForm as CommentFormI, BanFromCommunityForm, CommunityUser, AddModToCommunityForm } from '../interfaces';
|
import { CommentNode as CommentNodeI, CommentLikeForm, CommentForm as CommentFormI, SaveCommentForm, BanFromCommunityForm, BanUserForm, CommunityUser, UserView, AddModToCommunityForm, AddAdminForm } from '../interfaces';
|
||||||
import { WebSocketService, UserService } from '../services';
|
import { WebSocketService, UserService } from '../services';
|
||||||
import { mdToHtml, getUnixTime } from '../utils';
|
import { mdToHtml, getUnixTime, canMod, isMod } from '../utils';
|
||||||
import { MomentTime } from './moment-time';
|
import { MomentTime } from './moment-time';
|
||||||
import { CommentForm } from './comment-form';
|
import { CommentForm } from './comment-form';
|
||||||
import { CommentNodes } from './comment-nodes';
|
import { CommentNodes } from './comment-nodes';
|
||||||
|
|
||||||
|
enum BanType {Community, Site};
|
||||||
|
|
||||||
interface CommentNodeState {
|
interface CommentNodeState {
|
||||||
showReply: boolean;
|
showReply: boolean;
|
||||||
showEdit: boolean;
|
showEdit: boolean;
|
||||||
|
@ -15,6 +17,7 @@ interface CommentNodeState {
|
||||||
showBanDialog: boolean;
|
showBanDialog: boolean;
|
||||||
banReason: string;
|
banReason: string;
|
||||||
banExpires: string;
|
banExpires: string;
|
||||||
|
banType: BanType;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CommentNodeProps {
|
interface CommentNodeProps {
|
||||||
|
@ -23,6 +26,7 @@ interface CommentNodeProps {
|
||||||
viewOnly?: boolean;
|
viewOnly?: boolean;
|
||||||
locked?: boolean;
|
locked?: boolean;
|
||||||
moderators: Array<CommunityUser>;
|
moderators: Array<CommunityUser>;
|
||||||
|
admins: Array<UserView>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
|
@ -35,6 +39,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
showBanDialog: false,
|
showBanDialog: false,
|
||||||
banReason: null,
|
banReason: null,
|
||||||
banExpires: null,
|
banExpires: null,
|
||||||
|
banType: BanType.Community
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
|
@ -60,6 +65,12 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
<li className="list-inline-item">
|
<li className="list-inline-item">
|
||||||
<Link className="text-info" to={`/user/${node.comment.creator_id}`}>{node.comment.creator_name}</Link>
|
<Link className="text-info" to={`/user/${node.comment.creator_id}`}>{node.comment.creator_name}</Link>
|
||||||
</li>
|
</li>
|
||||||
|
{this.isMod &&
|
||||||
|
<li className="list-inline-item badge badge-secondary">mod</li>
|
||||||
|
}
|
||||||
|
{this.isAdmin &&
|
||||||
|
<li className="list-inline-item badge badge-secondary">admin</li>
|
||||||
|
}
|
||||||
<li className="list-inline-item">
|
<li className="list-inline-item">
|
||||||
<span>(
|
<span>(
|
||||||
<span className="text-info">+{node.comment.upvotes}</span>
|
<span className="text-info">+{node.comment.upvotes}</span>
|
||||||
|
@ -77,47 +88,70 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
<div>
|
<div>
|
||||||
<div className="md-div" dangerouslySetInnerHTML={mdToHtml(node.comment.removed ? '*removed*' : node.comment.content)} />
|
<div className="md-div" dangerouslySetInnerHTML={mdToHtml(node.comment.removed ? '*removed*' : node.comment.content)} />
|
||||||
<ul class="list-inline mb-1 text-muted small font-weight-bold">
|
<ul class="list-inline mb-1 text-muted small font-weight-bold">
|
||||||
{!this.props.viewOnly &&
|
{UserService.Instance.user && !this.props.viewOnly &&
|
||||||
<span class="mr-2">
|
<>
|
||||||
<li className="list-inline-item">
|
<li className="list-inline-item">
|
||||||
<span class="pointer" onClick={linkEvent(this, this.handleReplyClick)}>reply</span>
|
<span class="pointer" onClick={linkEvent(this, this.handleReplyClick)}>reply</span>
|
||||||
</li>
|
</li>
|
||||||
|
<li className="list-inline-item mr-2">
|
||||||
|
<span class="pointer" onClick={linkEvent(this, this.handleSaveCommentClick)}>{node.comment.saved ? 'unsave' : 'save'}</span>
|
||||||
|
</li>
|
||||||
{this.myComment &&
|
{this.myComment &&
|
||||||
<>
|
<>
|
||||||
<li className="list-inline-item">
|
<li className="list-inline-item">
|
||||||
<span class="pointer" onClick={linkEvent(this, this.handleEditClick)}>edit</span>
|
<span class="pointer" onClick={linkEvent(this, this.handleEditClick)}>edit</span>
|
||||||
</li>
|
</li>
|
||||||
<li className="list-inline-item">
|
<li className="list-inline-item">
|
||||||
<span class="pointer" onClick={linkEvent(this, this.handleDeleteClick)}>delete</span>
|
<span class="pointer" onClick={linkEvent(this, this.handleDeleteClick)}>delete</span>
|
||||||
</li>
|
</li>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
{/* Admins and mods can remove comments */}
|
||||||
{this.canMod &&
|
{this.canMod &&
|
||||||
<>
|
|
||||||
<li className="list-inline-item">
|
<li className="list-inline-item">
|
||||||
{!this.props.node.comment.removed ?
|
{!this.props.node.comment.removed ?
|
||||||
<span class="pointer" onClick={linkEvent(this, this.handleModRemoveShow)}>remove</span> :
|
<span class="pointer" onClick={linkEvent(this, this.handleModRemoveShow)}>remove</span> :
|
||||||
<span class="pointer" onClick={linkEvent(this, this.handleModRemoveSubmit)}>restore</span>
|
<span class="pointer" onClick={linkEvent(this, this.handleModRemoveSubmit)}>restore</span>
|
||||||
}
|
}
|
||||||
</li>
|
</li>
|
||||||
{!this.isMod &&
|
}
|
||||||
<>
|
{/* Mods can ban from community, and appoint as mods to community */}
|
||||||
|
{this.canMod &&
|
||||||
|
<>
|
||||||
|
{!this.isMod &&
|
||||||
<li className="list-inline-item">
|
<li className="list-inline-item">
|
||||||
{!this.props.node.comment.banned ?
|
{!this.props.node.comment.banned_from_community ?
|
||||||
<span class="pointer" onClick={linkEvent(this, this.handleModBanShow)}>ban</span> :
|
<span class="pointer" onClick={linkEvent(this, this.handleModBanFromCommunityShow)}>ban</span> :
|
||||||
<span class="pointer" onClick={linkEvent(this, this.handleModBanSubmit)}>unban</span>
|
<span class="pointer" onClick={linkEvent(this, this.handleModBanFromCommunitySubmit)}>unban</span>
|
||||||
}
|
}
|
||||||
</li>
|
</li>
|
||||||
</>
|
}
|
||||||
}
|
{!this.props.node.comment.banned_from_community &&
|
||||||
{!this.props.node.comment.banned &&
|
<li className="list-inline-item">
|
||||||
<li className="list-inline-item">
|
<span class="pointer" onClick={linkEvent(this, this.handleAddModToCommunity)}>{`${this.isMod ? 'remove' : 'appoint'} as mod`}</span>
|
||||||
<span class="pointer" onClick={linkEvent(this, this.handleAddModToCommunity)}>{`${this.isMod ? 'remove' : 'appoint'} as mod`}</span>
|
</li>
|
||||||
</li>
|
}
|
||||||
}
|
</>
|
||||||
</>
|
|
||||||
}
|
}
|
||||||
</span>
|
{/* Admins can ban from all, and appoint other admins */}
|
||||||
|
{this.canAdmin &&
|
||||||
|
<>
|
||||||
|
{!this.isAdmin &&
|
||||||
|
<li className="list-inline-item">
|
||||||
|
{!this.props.node.comment.banned ?
|
||||||
|
<span class="pointer" onClick={linkEvent(this, this.handleModBanShow)}>ban from site</span> :
|
||||||
|
<span class="pointer" onClick={linkEvent(this, this.handleModBanSubmit)}>unban from site</span>
|
||||||
|
}
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
{!this.props.node.comment.banned &&
|
||||||
|
<li className="list-inline-item">
|
||||||
|
<span class="pointer" onClick={linkEvent(this, this.addAdmin)}>{`${this.isAdmin ? 'remove' : 'appoint'} as admin`}</span>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
</>
|
||||||
}
|
}
|
||||||
<li className="list-inline-item">
|
<li className="list-inline-item">
|
||||||
<Link className="text-muted" to={`/post/${node.comment.post_id}/comment/${node.comment.id}`} target="_blank">link</Link>
|
<Link className="text-muted" to={`/post/${node.comment.post_id}/comment/${node.comment.id}`} target="_blank">link</Link>
|
||||||
|
@ -133,22 +167,35 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
</form>
|
</form>
|
||||||
}
|
}
|
||||||
{this.state.showBanDialog &&
|
{this.state.showBanDialog &&
|
||||||
<form onSubmit={linkEvent(this, this.handleModBanSubmit)}>
|
<form onSubmit={linkEvent(this, this.handleModBanBothSubmit)}>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label class="col-form-label">Reason</label>
|
<label class="col-form-label">Reason</label>
|
||||||
<input type="text" class="form-control mr-2" placeholder="Optional" value={this.state.banReason} onInput={linkEvent(this, this.handleModBanReasonChange)} />
|
<input type="text" class="form-control mr-2" placeholder="Optional" value={this.state.banReason} onInput={linkEvent(this, this.handleModBanReasonChange)} />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label class="col-form-label">Expires</label>
|
<label class="col-form-label">Expires</label>
|
||||||
<input type="date" class="form-control mr-2" placeholder="Expires" value={this.state.banExpires} onInput={linkEvent(this, this.handleModBanExpiresChange)} />
|
<input type="date" class="form-control mr-2" placeholder="Expires" value={this.state.banExpires} onInput={linkEvent(this, this.handleModBanExpiresChange)} />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<button type="submit" class="btn btn-secondary">Ban {this.props.node.comment.creator_name}</button>
|
<button type="submit" class="btn btn-secondary">Ban {this.props.node.comment.creator_name}</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
}
|
||||||
|
{this.state.showReply &&
|
||||||
|
<CommentForm
|
||||||
|
node={node}
|
||||||
|
onReplyCancel={this.handleReplyCancel}
|
||||||
|
disabled={this.props.locked}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
{this.props.node.children &&
|
||||||
|
<CommentNodes
|
||||||
|
nodes={this.props.node.children}
|
||||||
|
locked={this.props.locked}
|
||||||
|
moderators={this.props.moderators}
|
||||||
|
admins={this.props.admins}
|
||||||
|
/>
|
||||||
}
|
}
|
||||||
{this.state.showReply && <CommentForm node={node} onReplyCancel={this.handleReplyCancel} disabled={this.props.locked} />}
|
|
||||||
{this.props.node.children && <CommentNodes nodes={this.props.node.children} locked={this.props.locked} moderators={this.props.moderators}/>}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -158,27 +205,22 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
get canMod(): boolean {
|
get canMod(): boolean {
|
||||||
|
let adminsThenMods = this.props.admins.map(a => a.id)
|
||||||
|
.concat(this.props.moderators.map(m => m.user_id));
|
||||||
|
|
||||||
// You can do moderator actions only on the mods added after you.
|
return canMod(UserService.Instance.user, adminsThenMods, this.props.node.comment.creator_id);
|
||||||
if (UserService.Instance.user) {
|
|
||||||
let modIds = this.props.moderators.map(m => m.user_id);
|
|
||||||
let yourIndex = modIds.findIndex(id => id == UserService.Instance.user.id);
|
|
||||||
if (yourIndex == -1) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
console.log(modIds);
|
|
||||||
modIds = modIds.slice(0, yourIndex+1); // +1 cause you cant mod yourself
|
|
||||||
console.log(modIds);
|
|
||||||
return !modIds.includes(this.props.node.comment.creator_id);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get isMod(): boolean {
|
get isMod(): boolean {
|
||||||
return this.props.moderators.map(m => m.user_id).includes(this.props.node.comment.creator_id);
|
return isMod(this.props.moderators.map(m => m.user_id), this.props.node.comment.creator_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
get isAdmin(): boolean {
|
||||||
|
return isMod(this.props.admins.map(a => a.id), this.props.node.comment.creator_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
get canAdmin(): boolean {
|
||||||
|
return canMod(UserService.Instance.user, this.props.admins.map(a => a.id), this.props.node.comment.creator_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleReplyClick(i: CommentNode) {
|
handleReplyClick(i: CommentNode) {
|
||||||
|
@ -193,16 +235,27 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
|
|
||||||
handleDeleteClick(i: CommentNode) {
|
handleDeleteClick(i: CommentNode) {
|
||||||
let deleteForm: CommentFormI = {
|
let deleteForm: CommentFormI = {
|
||||||
content: "*deleted*",
|
content: '*deleted*',
|
||||||
edit_id: i.props.node.comment.id,
|
edit_id: i.props.node.comment.id,
|
||||||
creator_id: i.props.node.comment.creator_id,
|
creator_id: i.props.node.comment.creator_id,
|
||||||
post_id: i.props.node.comment.post_id,
|
post_id: i.props.node.comment.post_id,
|
||||||
parent_id: i.props.node.comment.parent_id,
|
parent_id: i.props.node.comment.parent_id,
|
||||||
|
removed: i.props.node.comment.removed,
|
||||||
auth: null
|
auth: null
|
||||||
};
|
};
|
||||||
WebSocketService.Instance.editComment(deleteForm);
|
WebSocketService.Instance.editComment(deleteForm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleSaveCommentClick(i: CommentNode) {
|
||||||
|
let saved = (i.props.node.comment.saved == undefined) ? true : !i.props.node.comment.saved;
|
||||||
|
let form: SaveCommentForm = {
|
||||||
|
comment_id: i.props.node.comment.id,
|
||||||
|
save: saved
|
||||||
|
};
|
||||||
|
|
||||||
|
WebSocketService.Instance.saveComment(form);
|
||||||
|
}
|
||||||
|
|
||||||
handleReplyCancel() {
|
handleReplyCancel() {
|
||||||
this.state.showReply = false;
|
this.state.showReply = false;
|
||||||
this.state.showEdit = false;
|
this.state.showEdit = false;
|
||||||
|
@ -257,8 +310,15 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleModBanFromCommunityShow(i: CommentNode) {
|
||||||
|
i.state.showBanDialog = true;
|
||||||
|
i.state.banType = BanType.Community;
|
||||||
|
i.setState(i.state);
|
||||||
|
}
|
||||||
|
|
||||||
handleModBanShow(i: CommentNode) {
|
handleModBanShow(i: CommentNode) {
|
||||||
i.state.showBanDialog = true;
|
i.state.showBanDialog = true;
|
||||||
|
i.state.banType = BanType.Site;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,16 +332,42 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleModBanFromCommunitySubmit(i: CommentNode) {
|
||||||
|
i.state.banType = BanType.Community;
|
||||||
|
i.setState(i.state);
|
||||||
|
i.handleModBanBothSubmit(i);
|
||||||
|
}
|
||||||
|
|
||||||
handleModBanSubmit(i: CommentNode) {
|
handleModBanSubmit(i: CommentNode) {
|
||||||
|
i.state.banType = BanType.Site;
|
||||||
|
i.setState(i.state);
|
||||||
|
i.handleModBanBothSubmit(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleModBanBothSubmit(i: CommentNode) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
let form: BanFromCommunityForm = {
|
|
||||||
user_id: i.props.node.comment.creator_id,
|
console.log(BanType[i.state.banType]);
|
||||||
community_id: i.props.node.comment.community_id,
|
console.log(i.props.node.comment.banned);
|
||||||
ban: !i.props.node.comment.banned,
|
|
||||||
reason: i.state.banReason,
|
if (i.state.banType == BanType.Community) {
|
||||||
expires: getUnixTime(i.state.banExpires),
|
let form: BanFromCommunityForm = {
|
||||||
};
|
user_id: i.props.node.comment.creator_id,
|
||||||
WebSocketService.Instance.banFromCommunity(form);
|
community_id: i.props.node.comment.community_id,
|
||||||
|
ban: !i.props.node.comment.banned_from_community,
|
||||||
|
reason: i.state.banReason,
|
||||||
|
expires: getUnixTime(i.state.banExpires),
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.banFromCommunity(form);
|
||||||
|
} else {
|
||||||
|
let form: BanUserForm = {
|
||||||
|
user_id: i.props.node.comment.creator_id,
|
||||||
|
ban: !i.props.node.comment.banned,
|
||||||
|
reason: i.state.banReason,
|
||||||
|
expires: getUnixTime(i.state.banExpires),
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.banUser(form);
|
||||||
|
}
|
||||||
|
|
||||||
i.state.showBanDialog = false;
|
i.state.showBanDialog = false;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
|
@ -296,4 +382,13 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
WebSocketService.Instance.addModToCommunity(form);
|
WebSocketService.Instance.addModToCommunity(form);
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addAdmin(i: CommentNode) {
|
||||||
|
let form: AddAdminForm = {
|
||||||
|
user_id: i.props.node.comment.creator_id,
|
||||||
|
added: !i.isAdmin,
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.addAdmin(form);
|
||||||
|
i.setState(i.state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Component } from 'inferno';
|
import { Component } from 'inferno';
|
||||||
import { CommentNode as CommentNodeI, CommunityUser } from '../interfaces';
|
import { CommentNode as CommentNodeI, CommunityUser, UserView } from '../interfaces';
|
||||||
import { CommentNode } from './comment-node';
|
import { CommentNode } from './comment-node';
|
||||||
|
|
||||||
interface CommentNodesState {
|
interface CommentNodesState {
|
||||||
|
@ -8,6 +8,7 @@ interface CommentNodesState {
|
||||||
interface CommentNodesProps {
|
interface CommentNodesProps {
|
||||||
nodes: Array<CommentNodeI>;
|
nodes: Array<CommentNodeI>;
|
||||||
moderators?: Array<CommunityUser>;
|
moderators?: Array<CommunityUser>;
|
||||||
|
admins?: Array<UserView>;
|
||||||
noIndent?: boolean;
|
noIndent?: boolean;
|
||||||
viewOnly?: boolean;
|
viewOnly?: boolean;
|
||||||
locked?: boolean;
|
locked?: boolean;
|
||||||
|
@ -27,7 +28,9 @@ export class CommentNodes extends Component<CommentNodesProps, CommentNodesState
|
||||||
noIndent={this.props.noIndent}
|
noIndent={this.props.noIndent}
|
||||||
viewOnly={this.props.viewOnly}
|
viewOnly={this.props.viewOnly}
|
||||||
locked={this.props.locked}
|
locked={this.props.locked}
|
||||||
moderators={this.props.moderators}/>
|
moderators={this.props.moderators}
|
||||||
|
admins={this.props.admins}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -53,9 +53,9 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div class="container">
|
||||||
{this.state.loading ?
|
{this.state.loading ?
|
||||||
<h4 class=""><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h4> :
|
<h5 class=""><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h5> :
|
||||||
<div>
|
<div>
|
||||||
<h4>Communities</h4>
|
<h5>Communities</h5>
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table id="community_table" class="table table-sm table-hover">
|
<table id="community_table" class="table table-sm table-hover">
|
||||||
<thead class="pointer">
|
<thead class="pointer">
|
||||||
|
|
|
@ -60,14 +60,14 @@ export class Community extends Component<any, State> {
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div class="container">
|
||||||
{this.state.loading ?
|
{this.state.loading ?
|
||||||
<h4><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h4> :
|
<h5><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h5> :
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-md-9">
|
<div class="col-12 col-md-9">
|
||||||
<h4>{this.state.community.title}
|
<h5>{this.state.community.title}
|
||||||
{this.state.community.removed &&
|
{this.state.community.removed &&
|
||||||
<small className="ml-2 text-muted font-italic">removed</small>
|
<small className="ml-2 text-muted font-italic">removed</small>
|
||||||
}
|
}
|
||||||
</h4>
|
</h5>
|
||||||
<PostListings communityId={this.state.communityId} />
|
<PostListings communityId={this.state.communityId} />
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-3">
|
<div class="col-12 col-md-3">
|
||||||
|
|
|
@ -13,7 +13,7 @@ export class CreateCommunity extends Component<any, any> {
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-lg-6 mb-4">
|
<div class="col-12 col-lg-6 mb-4">
|
||||||
<h4>Create Forum</h4>
|
<h5>Create Forum</h5>
|
||||||
<CommunityForm onCreate={this.handleCommunityCreate}/>
|
<CommunityForm onCreate={this.handleCommunityCreate}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -13,7 +13,7 @@ export class CreatePost extends Component<any, any> {
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-lg-6 mb-4">
|
<div class="col-12 col-lg-6 mb-4">
|
||||||
<h4>Create a Post</h4>
|
<h5>Create a Post</h5>
|
||||||
<PostForm onCreate={this.handlePostCreate}/>
|
<PostForm onCreate={this.handlePostCreate}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -67,7 +67,7 @@ export class Login extends Component<any, State> {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<form onSubmit={linkEvent(this, this.handleLoginSubmit)}>
|
<form onSubmit={linkEvent(this, this.handleLoginSubmit)}>
|
||||||
<h4>Login</h4>
|
<h5>Login</h5>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label class="col-sm-2 col-form-label">Email or Username</label>
|
<label class="col-sm-2 col-form-label">Email or Username</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
|
@ -94,7 +94,7 @@ export class Login extends Component<any, State> {
|
||||||
registerForm() {
|
registerForm() {
|
||||||
return (
|
return (
|
||||||
<form onSubmit={linkEvent(this, this.handleRegisterSubmit)}>
|
<form onSubmit={linkEvent(this, this.handleRegisterSubmit)}>
|
||||||
<h4>Sign Up</h4>
|
<h5>Sign Up</h5>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label class="col-sm-2 col-form-label">Username</label>
|
<label class="col-sm-2 col-form-label">Username</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
|
|
|
@ -78,12 +78,12 @@ export class Main extends Component<any, State> {
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-4">
|
<div class="col-12 col-md-4">
|
||||||
{this.state.loading ?
|
{this.state.loading ?
|
||||||
<h4><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h4> :
|
<h5><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h5> :
|
||||||
<div>
|
<div>
|
||||||
{this.trendingCommunities()}
|
{this.trendingCommunities()}
|
||||||
{UserService.Instance.user && this.state.subscribedCommunities.length > 0 &&
|
{UserService.Instance.user && this.state.subscribedCommunities.length > 0 &&
|
||||||
<div>
|
<div>
|
||||||
<h4>Subscribed forums</h4>
|
<h5>Subscribed forums</h5>
|
||||||
<ul class="list-inline">
|
<ul class="list-inline">
|
||||||
{this.state.subscribedCommunities.map(community =>
|
{this.state.subscribedCommunities.map(community =>
|
||||||
<li class="list-inline-item"><Link to={`/community/${community.community_id}`}>{community.community_name}</Link></li>
|
<li class="list-inline-item"><Link to={`/community/${community.community_id}`}>{community.community_name}</Link></li>
|
||||||
|
@ -103,7 +103,7 @@ export class Main extends Component<any, State> {
|
||||||
trendingCommunities() {
|
trendingCommunities() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h4>Trending <Link class="text-white" to="/communities">forums</Link></h4>
|
<h5>Trending <Link class="text-white" to="/communities">forums</Link></h5>
|
||||||
<ul class="list-inline">
|
<ul class="list-inline">
|
||||||
{this.state.trendingCommunities.map(community =>
|
{this.state.trendingCommunities.map(community =>
|
||||||
<li class="list-inline-item"><Link to={`/community/${community.id}`}>{community.name}</Link></li>
|
<li class="list-inline-item"><Link to={`/community/${community.id}`}>{community.name}</Link></li>
|
||||||
|
@ -116,7 +116,7 @@ export class Main extends Component<any, State> {
|
||||||
landing() {
|
landing() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h4>{`${this.state.site.site.name}`}</h4>
|
<h5>{`${this.state.site.site.name}`}</h5>
|
||||||
<ul class="my-1 list-inline">
|
<ul class="my-1 list-inline">
|
||||||
<li className="list-inline-item badge badge-light">{this.state.site.site.number_of_users} Users</li>
|
<li className="list-inline-item badge badge-light">{this.state.site.site.number_of_users} Users</li>
|
||||||
<li className="list-inline-item badge badge-light">{this.state.site.site.number_of_posts} Posts</li>
|
<li className="list-inline-item badge badge-light">{this.state.site.site.number_of_posts} Posts</li>
|
||||||
|
@ -136,10 +136,10 @@ export class Main extends Component<any, State> {
|
||||||
<hr />
|
<hr />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<h4>Welcome to
|
<h5>Welcome to
|
||||||
<svg class="icon mx-2"><use xlinkHref="#icon-mouse"></use></svg>
|
<svg class="icon mx-2"><use xlinkHref="#icon-mouse"></use></svg>
|
||||||
<a href={repoUrl}>Lemmy<sup>Beta</sup></a>
|
<a href={repoUrl}>Lemmy<sup>Beta</sup></a>
|
||||||
</h4>
|
</h5>
|
||||||
<p>Lemmy is a <a href="https://en.wikipedia.org/wiki/Link_aggregation">link aggregator</a> / reddit alternative, intended to work in the <a href="https://en.wikipedia.org/wiki/Fediverse">fediverse</a>.</p>
|
<p>Lemmy is a <a href="https://en.wikipedia.org/wiki/Link_aggregation">link aggregator</a> / reddit alternative, intended to work in the <a href="https://en.wikipedia.org/wiki/Fediverse">fediverse</a>.</p>
|
||||||
<p>Its self-hostable, has live-updating comment threads, and is tiny (<code>~80kB</code>). Federation into the ActivityPub network is on the roadmap.</p>
|
<p>Its self-hostable, has live-updating comment threads, and is tiny (<code>~80kB</code>). Federation into the ActivityPub network is on the roadmap.</p>
|
||||||
<p>This is a <b>very early beta version</b>, and a lot of features are currently broken or missing.</p>
|
<p>This is a <b>very early beta version</b>, and a lot of features are currently broken or missing.</p>
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { MomentTime } from './moment-time';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
|
|
||||||
interface ModlogState {
|
interface ModlogState {
|
||||||
combined: Array<{type_: string, data: ModRemovePost | ModLockPost | ModRemoveCommunity}>,
|
combined: Array<{type_: string, data: ModRemovePost | ModLockPost | ModRemoveCommunity | ModAdd | ModBan}>,
|
||||||
communityId?: number,
|
communityId?: number,
|
||||||
communityName?: string,
|
communityName?: string,
|
||||||
page: number;
|
page: number;
|
||||||
|
@ -51,6 +51,8 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
let removed_communities = addTypeInfo(res.removed_communities, "removed_communities");
|
let removed_communities = addTypeInfo(res.removed_communities, "removed_communities");
|
||||||
let banned_from_community = addTypeInfo(res.banned_from_community, "banned_from_community");
|
let banned_from_community = addTypeInfo(res.banned_from_community, "banned_from_community");
|
||||||
let added_to_community = addTypeInfo(res.added_to_community, "added_to_community");
|
let added_to_community = addTypeInfo(res.added_to_community, "added_to_community");
|
||||||
|
let added = addTypeInfo(res.added, "added");
|
||||||
|
let banned = addTypeInfo(res.banned, "banned");
|
||||||
this.state.combined = [];
|
this.state.combined = [];
|
||||||
|
|
||||||
this.state.combined.push(...removed_posts);
|
this.state.combined.push(...removed_posts);
|
||||||
|
@ -59,9 +61,11 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
this.state.combined.push(...removed_communities);
|
this.state.combined.push(...removed_communities);
|
||||||
this.state.combined.push(...banned_from_community);
|
this.state.combined.push(...banned_from_community);
|
||||||
this.state.combined.push(...added_to_community);
|
this.state.combined.push(...added_to_community);
|
||||||
|
this.state.combined.push(...added);
|
||||||
|
this.state.combined.push(...banned);
|
||||||
|
|
||||||
if (this.state.communityId && this.state.combined.length > 0) {
|
if (this.state.communityId && this.state.combined.length > 0) {
|
||||||
this.state.communityName = this.state.combined[0].data.community_name;
|
this.state.communityName = (this.state.combined[0].data as ModRemovePost).community_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort them by time
|
// Sort them by time
|
||||||
|
@ -95,13 +99,14 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
<>
|
<>
|
||||||
{(i.data as ModRemoveComment).removed? 'Removed' : 'Restored'}
|
{(i.data as ModRemoveComment).removed? 'Removed' : 'Restored'}
|
||||||
<span> Comment <Link to={`/post/${(i.data as ModRemoveComment).post_id}/comment/${(i.data as ModRemoveComment).comment_id}`}>{(i.data as ModRemoveComment).comment_content}</Link></span>
|
<span> Comment <Link to={`/post/${(i.data as ModRemoveComment).post_id}/comment/${(i.data as ModRemoveComment).comment_id}`}>{(i.data as ModRemoveComment).comment_content}</Link></span>
|
||||||
|
<span> by <Link to={`/user/${(i.data as ModRemoveComment).comment_user_id}`}>{(i.data as ModRemoveComment).comment_user_name}</Link></span>
|
||||||
<div>{(i.data as ModRemoveComment).reason && ` reason: ${(i.data as ModRemoveComment).reason}`}</div>
|
<div>{(i.data as ModRemoveComment).reason && ` reason: ${(i.data as ModRemoveComment).reason}`}</div>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
{i.type_ == 'removed_communities' &&
|
{i.type_ == 'removed_communities' &&
|
||||||
<>
|
<>
|
||||||
{(i.data as ModRemoveCommunity).removed ? 'Removed' : 'Restored'}
|
{(i.data as ModRemoveCommunity).removed ? 'Removed' : 'Restored'}
|
||||||
<span> Community <Link to={`/community/${i.data.community_id}`}>{i.data.community_name}</Link></span>
|
<span> Community <Link to={`/community/${(i.data as ModRemoveCommunity).community_id}`}>{(i.data as ModRemoveCommunity).community_name}</Link></span>
|
||||||
<div>{(i.data as ModRemoveCommunity).reason && ` reason: ${(i.data as ModRemoveCommunity).reason}`}</div>
|
<div>{(i.data as ModRemoveCommunity).reason && ` reason: ${(i.data as ModRemoveCommunity).reason}`}</div>
|
||||||
<div>{(i.data as ModRemoveCommunity).expires && ` expires: ${moment.utc((i.data as ModRemoveCommunity).expires).fromNow()}`}</div>
|
<div>{(i.data as ModRemoveCommunity).expires && ` expires: ${moment.utc((i.data as ModRemoveCommunity).expires).fromNow()}`}</div>
|
||||||
</>
|
</>
|
||||||
|
@ -110,6 +115,8 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
<>
|
<>
|
||||||
<span>{(i.data as ModBanFromCommunity).banned ? 'Banned ' : 'Unbanned '} </span>
|
<span>{(i.data as ModBanFromCommunity).banned ? 'Banned ' : 'Unbanned '} </span>
|
||||||
<span><Link to={`/user/${(i.data as ModBanFromCommunity).other_user_id}`}>{(i.data as ModBanFromCommunity).other_user_name}</Link></span>
|
<span><Link to={`/user/${(i.data as ModBanFromCommunity).other_user_id}`}>{(i.data as ModBanFromCommunity).other_user_name}</Link></span>
|
||||||
|
<span> from the community </span>
|
||||||
|
<span><Link to={`/community/${(i.data as ModBanFromCommunity).community_id}`}>{(i.data as ModBanFromCommunity).community_name}</Link></span>
|
||||||
<div>{(i.data as ModBanFromCommunity).reason && ` reason: ${(i.data as ModBanFromCommunity).reason}`}</div>
|
<div>{(i.data as ModBanFromCommunity).reason && ` reason: ${(i.data as ModBanFromCommunity).reason}`}</div>
|
||||||
<div>{(i.data as ModBanFromCommunity).expires && ` expires: ${moment.utc((i.data as ModBanFromCommunity).expires).fromNow()}`}</div>
|
<div>{(i.data as ModBanFromCommunity).expires && ` expires: ${moment.utc((i.data as ModBanFromCommunity).expires).fromNow()}`}</div>
|
||||||
</>
|
</>
|
||||||
|
@ -119,12 +126,27 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
<span>{(i.data as ModAddCommunity).removed ? 'Removed ' : 'Appointed '} </span>
|
<span>{(i.data as ModAddCommunity).removed ? 'Removed ' : 'Appointed '} </span>
|
||||||
<span><Link to={`/user/${(i.data as ModAddCommunity).other_user_id}`}>{(i.data as ModAddCommunity).other_user_name}</Link></span>
|
<span><Link to={`/user/${(i.data as ModAddCommunity).other_user_id}`}>{(i.data as ModAddCommunity).other_user_name}</Link></span>
|
||||||
<span> as a mod to the community </span>
|
<span> as a mod to the community </span>
|
||||||
<span><Link to={`/community/${i.data.community_id}`}>{i.data.community_name}</Link></span>
|
<span><Link to={`/community/${(i.data as ModAddCommunity).community_id}`}>{(i.data as ModAddCommunity).community_name}</Link></span>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
{i.type_ == 'banned' &&
|
||||||
|
<>
|
||||||
|
<span>{(i.data as ModBan).banned ? 'Banned ' : 'Unbanned '} </span>
|
||||||
|
<span><Link to={`/user/${(i.data as ModBan).other_user_id}`}>{(i.data as ModBan).other_user_name}</Link></span>
|
||||||
|
<div>{(i.data as ModBan).reason && ` reason: ${(i.data as ModBan).reason}`}</div>
|
||||||
|
<div>{(i.data as ModBan).expires && ` expires: ${moment.utc((i.data as ModBan).expires).fromNow()}`}</div>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
{i.type_ == 'added' &&
|
||||||
|
<>
|
||||||
|
<span>{(i.data as ModAdd).removed ? 'Removed ' : 'Appointed '} </span>
|
||||||
|
<span><Link to={`/user/${(i.data as ModAdd).other_user_id}`}>{(i.data as ModAdd).other_user_name}</Link></span>
|
||||||
|
<span> as an admin </span>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -136,12 +158,12 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div class="container">
|
||||||
{this.state.loading ?
|
{this.state.loading ?
|
||||||
<h4 class=""><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h4> :
|
<h5 class=""><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h5> :
|
||||||
<div>
|
<div>
|
||||||
<h4>
|
<h5>
|
||||||
{this.state.communityName && <Link className="text-white" to={`/community/${this.state.communityId}`}>/f/{this.state.communityName} </Link>}
|
{this.state.communityName && <Link className="text-white" to={`/community/${this.state.communityId}`}>/f/{this.state.communityName} </Link>}
|
||||||
<span>Modlog</span>
|
<span>Modlog</span>
|
||||||
</h4>
|
</h5>
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table id="modlog_table" class="table table-sm table-hover">
|
<table id="modlog_table" class="table table-sm table-hover">
|
||||||
<thead class="pointer">
|
<thead class="pointer">
|
||||||
|
|
|
@ -64,16 +64,22 @@ export class Navbar extends Component<any, NavbarState> {
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="navbar-nav ml-auto mr-2">
|
<ul class="navbar-nav ml-auto mr-2">
|
||||||
{this.state.isLoggedIn ?
|
{this.state.isLoggedIn ?
|
||||||
<li className={`nav-item dropdown ${this.state.expandUserDropdown && 'show'}`}>
|
<>
|
||||||
<a class="pointer nav-link dropdown-toggle" onClick={linkEvent(this, this.expandUserDropdown)} role="button">
|
<li className="nav-item">
|
||||||
{UserService.Instance.user.username}
|
<Link class="nav-link" to="/communities">🖂</Link>
|
||||||
</a>
|
</li>
|
||||||
<div className={`dropdown-menu dropdown-menu-right ${this.state.expandUserDropdown && 'show'}`}>
|
<li className={`nav-item dropdown ${this.state.expandUserDropdown && 'show'}`}>
|
||||||
<a role="button" class="dropdown-item pointer" onClick={linkEvent(this, this.handleOverviewClick)}>Overview</a>
|
<a class="pointer nav-link dropdown-toggle" onClick={linkEvent(this, this.expandUserDropdown)} role="button">
|
||||||
<a role="button" class="dropdown-item pointer" onClick={ linkEvent(this, this.handleLogoutClick) }>Logout</a>
|
{UserService.Instance.user.username}
|
||||||
</div>
|
</a>
|
||||||
</li> :
|
<div className={`dropdown-menu dropdown-menu-right ${this.state.expandUserDropdown && 'show'}`}>
|
||||||
<Link class="nav-link" to="/login">Login / Sign up</Link>
|
<a role="button" class="dropdown-item pointer" onClick={linkEvent(this, this.handleOverviewClick)}>Overview</a>
|
||||||
|
<a role="button" class="dropdown-item pointer" onClick={ linkEvent(this, this.handleLogoutClick) }>Logout</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</>
|
||||||
|
:
|
||||||
|
<Link class="nav-link" to="/login">Login / Sign up</Link>
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Component, linkEvent } from 'inferno';
|
import { Component, linkEvent } from 'inferno';
|
||||||
import { Link } from 'inferno-router';
|
import { Link } from 'inferno-router';
|
||||||
import { WebSocketService, UserService } from '../services';
|
import { WebSocketService, UserService } from '../services';
|
||||||
import { Post, CreatePostLikeForm, PostForm as PostFormI } from '../interfaces';
|
import { Post, CreatePostLikeForm, PostForm as PostFormI, SavePostForm } from '../interfaces';
|
||||||
import { MomentTime } from './moment-time';
|
import { MomentTime } from './moment-time';
|
||||||
import { PostForm } from './post-form';
|
import { PostForm } from './post-form';
|
||||||
import { mdToHtml } from '../utils';
|
import { mdToHtml } from '../utils';
|
||||||
|
@ -60,17 +60,17 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
<div>{post.score}</div>
|
<div>{post.score}</div>
|
||||||
<div className={`pointer downvote ${post.my_vote == -1 && 'text-danger'}`} onClick={linkEvent(this, this.handlePostDisLike)}>▼</div>
|
<div className={`pointer downvote ${post.my_vote == -1 && 'text-danger'}`} onClick={linkEvent(this, this.handlePostDisLike)}>▼</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-4">
|
<div className="pt-1 ml-4">
|
||||||
{post.url
|
{post.url
|
||||||
? <div className="mb-0">
|
? <div className="mb-0">
|
||||||
<h4 className="d-inline"><a className="text-white" href={post.url} title={post.url}>{post.name}</a>
|
<h5 className="d-inline"><a className="text-white" href={post.url} title={post.url}>{post.name}</a>
|
||||||
{post.removed &&
|
{post.removed &&
|
||||||
<small className="ml-2 text-muted font-italic">removed</small>
|
<small className="ml-2 text-muted font-italic">removed</small>
|
||||||
}
|
}
|
||||||
{post.locked &&
|
{post.locked &&
|
||||||
<small className="ml-2 text-muted font-italic">locked</small>
|
<small className="ml-2 text-muted font-italic">locked</small>
|
||||||
}
|
}
|
||||||
</h4>
|
</h5>
|
||||||
<small><a className="ml-2 text-muted font-italic" href={post.url} title={post.url}>{(new URL(post.url)).hostname}</a></small>
|
<small><a className="ml-2 text-muted font-italic" href={post.url} title={post.url}>{(new URL(post.url)).hostname}</a></small>
|
||||||
{ !this.state.iframeExpanded
|
{ !this.state.iframeExpanded
|
||||||
? <span class="badge badge-light pointer ml-2 text-muted small" title="Expand here" onClick={linkEvent(this, this.handleIframeExpandClick)}>+</span>
|
? <span class="badge badge-light pointer ml-2 text-muted small" title="Expand here" onClick={linkEvent(this, this.handleIframeExpandClick)}>+</span>
|
||||||
|
@ -83,14 +83,14 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
: <h4 className="mb-0"><Link className="text-white" to={`/post/${post.id}`}>{post.name}</Link>
|
: <h5 className="mb-0"><Link className="text-white" to={`/post/${post.id}`}>{post.name}</Link>
|
||||||
{post.removed &&
|
{post.removed &&
|
||||||
<small className="ml-2 text-muted font-italic">removed</small>
|
<small className="ml-2 text-muted font-italic">removed</small>
|
||||||
}
|
}
|
||||||
{post.locked &&
|
{post.locked &&
|
||||||
<small className="ml-2 text-muted font-italic">locked</small>
|
<small className="ml-2 text-muted font-italic">locked</small>
|
||||||
}
|
}
|
||||||
</h4>
|
</h5>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div className="details ml-4 mb-1">
|
<div className="details ml-4 mb-1">
|
||||||
|
@ -120,17 +120,20 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
<Link className="text-muted" to={`/post/${post.id}`}>{post.number_of_comments} Comments</Link>
|
<Link className="text-muted" to={`/post/${post.id}`}>{post.number_of_comments} Comments</Link>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
{this.props.editable &&
|
{UserService.Instance.user && this.props.editable &&
|
||||||
<ul class="list-inline mb-1 text-muted small font-weight-bold">
|
<ul class="list-inline mb-1 text-muted small font-weight-bold">
|
||||||
|
<li className="list-inline-item mr-2">
|
||||||
|
<span class="pointer" onClick={linkEvent(this, this.handleSavePostClick)}>{this.props.post.saved ? 'unsave' : 'save'}</span>
|
||||||
|
</li>
|
||||||
{this.myPost &&
|
{this.myPost &&
|
||||||
<span>
|
<>
|
||||||
<li className="list-inline-item">
|
<li className="list-inline-item">
|
||||||
<span class="pointer" onClick={linkEvent(this, this.handleEditClick)}>edit</span>
|
<span class="pointer" onClick={linkEvent(this, this.handleEditClick)}>edit</span>
|
||||||
</li>
|
</li>
|
||||||
<li className="list-inline-item mr-2">
|
<li className="list-inline-item mr-2">
|
||||||
<span class="pointer" onClick={linkEvent(this, this.handleDeleteClick)}>delete</span>
|
<span class="pointer" onClick={linkEvent(this, this.handleDeleteClick)}>delete</span>
|
||||||
</li>
|
</li>
|
||||||
</span>
|
</>
|
||||||
}
|
}
|
||||||
{this.props.post.am_mod &&
|
{this.props.post.am_mod &&
|
||||||
<span>
|
<span>
|
||||||
|
@ -204,11 +207,23 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
url: '',
|
url: '',
|
||||||
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,
|
||||||
|
removed: !i.props.post.removed,
|
||||||
|
locked: !i.props.post.locked,
|
||||||
auth: null
|
auth: null
|
||||||
};
|
};
|
||||||
WebSocketService.Instance.editPost(deleteForm);
|
WebSocketService.Instance.editPost(deleteForm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleSavePostClick(i: PostListing) {
|
||||||
|
let saved = (i.props.post.saved == undefined) ? true : !i.props.post.saved;
|
||||||
|
let form: SavePostForm = {
|
||||||
|
post_id: i.props.post.id,
|
||||||
|
save: saved
|
||||||
|
};
|
||||||
|
|
||||||
|
WebSocketService.Instance.savePost(form);
|
||||||
|
}
|
||||||
|
|
||||||
handleModRemoveShow(i: PostListing) {
|
handleModRemoveShow(i: PostListing) {
|
||||||
i.state.showRemoveDialog = true;
|
i.state.showRemoveDialog = true;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
|
@ -227,6 +242,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,
|
||||||
removed: !i.props.post.removed,
|
removed: !i.props.post.removed,
|
||||||
|
locked: !i.props.post.locked,
|
||||||
reason: i.state.removeReason,
|
reason: i.state.removeReason,
|
||||||
auth: null,
|
auth: null,
|
||||||
};
|
};
|
||||||
|
@ -242,6 +258,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,
|
||||||
|
removed: !i.props.post.removed,
|
||||||
locked: !i.props.post.locked,
|
locked: !i.props.post.locked,
|
||||||
auth: null,
|
auth: null,
|
||||||
};
|
};
|
||||||
|
|
|
@ -61,7 +61,7 @@ export class PostListings extends Component<PostListingsProps, PostListingsState
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{this.state.loading ?
|
{this.state.loading ?
|
||||||
<h4><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h4> :
|
<h5><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h5> :
|
||||||
<div>
|
<div>
|
||||||
{this.selects()}
|
{this.selects()}
|
||||||
{this.state.posts.length > 0
|
{this.state.posts.length > 0
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Component, linkEvent } from 'inferno';
|
import { Component, linkEvent } from 'inferno';
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
import { retryWhen, delay, take } from 'rxjs/operators';
|
import { retryWhen, delay, take } from 'rxjs/operators';
|
||||||
import { UserOperation, Community, Post as PostI, GetPostResponse, PostResponse, Comment, CommentResponse, CommentSortType, CreatePostLikeResponse, CommunityUser, CommunityResponse, CommentNode as CommentNodeI, BanFromCommunityResponse, AddModToCommunityResponse } from '../interfaces';
|
import { UserOperation, Community, Post as PostI, GetPostResponse, PostResponse, Comment, CommentResponse, CommentSortType, CreatePostLikeResponse, CommunityUser, CommunityResponse, CommentNode as CommentNodeI, BanFromCommunityResponse, BanUserResponse, AddModToCommunityResponse, AddAdminResponse, UserView } from '../interfaces';
|
||||||
import { WebSocketService } from '../services';
|
import { WebSocketService } from '../services';
|
||||||
import { msgOp, hotRank } from '../utils';
|
import { msgOp, hotRank } from '../utils';
|
||||||
import { PostListing } from './post-listing';
|
import { PostListing } from './post-listing';
|
||||||
|
@ -17,6 +17,7 @@ interface PostState {
|
||||||
commentSort: CommentSortType;
|
commentSort: CommentSortType;
|
||||||
community: Community;
|
community: Community;
|
||||||
moderators: Array<CommunityUser>;
|
moderators: Array<CommunityUser>;
|
||||||
|
admins: Array<UserView>;
|
||||||
scrolled?: boolean;
|
scrolled?: boolean;
|
||||||
scrolled_comment_id?: number;
|
scrolled_comment_id?: number;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
|
@ -31,6 +32,7 @@ export class Post extends Component<any, PostState> {
|
||||||
commentSort: CommentSortType.Hot,
|
commentSort: CommentSortType.Hot,
|
||||||
community: null,
|
community: null,
|
||||||
moderators: [],
|
moderators: [],
|
||||||
|
admins: [],
|
||||||
scrolled: false,
|
scrolled: false,
|
||||||
loading: true
|
loading: true
|
||||||
}
|
}
|
||||||
|
@ -77,7 +79,7 @@ export class Post extends Component<any, PostState> {
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div class="container">
|
||||||
{this.state.loading ?
|
{this.state.loading ?
|
||||||
<h4><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h4> :
|
<h5><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h5> :
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-md-8 col-lg-7 mb-3">
|
<div class="col-12 col-md-8 col-lg-7 mb-3">
|
||||||
<PostListing post={this.state.post} showBody showCommunity editable />
|
<PostListing post={this.state.post} showBody showCommunity editable />
|
||||||
|
@ -123,9 +125,15 @@ export class Post extends Component<any, PostState> {
|
||||||
newComments() {
|
newComments() {
|
||||||
return (
|
return (
|
||||||
<div class="sticky-top">
|
<div class="sticky-top">
|
||||||
<h4>New Comments</h4>
|
<h5>New Comments</h5>
|
||||||
{this.state.comments.map(comment =>
|
{this.state.comments.map(comment =>
|
||||||
<CommentNodes nodes={[{comment: comment}]} noIndent locked={this.state.post.locked} moderators={this.state.moderators} />
|
<CommentNodes
|
||||||
|
nodes={[{comment: comment}]}
|
||||||
|
noIndent
|
||||||
|
locked={this.state.post.locked}
|
||||||
|
moderators={this.state.moderators}
|
||||||
|
admins={this.state.admins}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -187,8 +195,13 @@ export class Post extends Component<any, PostState> {
|
||||||
commentsTree() {
|
commentsTree() {
|
||||||
let nodes = this.buildCommentsTree();
|
let nodes = this.buildCommentsTree();
|
||||||
return (
|
return (
|
||||||
<div className="">
|
<div>
|
||||||
<CommentNodes nodes={nodes} locked={this.state.post.locked} moderators={this.state.moderators} />
|
<CommentNodes
|
||||||
|
nodes={nodes}
|
||||||
|
locked={this.state.post.locked}
|
||||||
|
moderators={this.state.moderators}
|
||||||
|
admins={this.state.admins}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -202,9 +215,11 @@ export class Post extends Component<any, PostState> {
|
||||||
} else if (op == UserOperation.GetPost) {
|
} else if (op == UserOperation.GetPost) {
|
||||||
let res: GetPostResponse = msg;
|
let res: GetPostResponse = msg;
|
||||||
this.state.post = res.post;
|
this.state.post = res.post;
|
||||||
|
this.state.post = res.post;
|
||||||
this.state.comments = res.comments;
|
this.state.comments = res.comments;
|
||||||
this.state.community = res.community;
|
this.state.community = res.community;
|
||||||
this.state.moderators = res.moderators;
|
this.state.moderators = res.moderators;
|
||||||
|
this.state.admins = res.admins;
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.CreateComment) {
|
} else if (op == UserOperation.CreateComment) {
|
||||||
|
@ -222,8 +237,12 @@ export class Post extends Component<any, PostState> {
|
||||||
found.score = res.comment.score;
|
found.score = res.comment.score;
|
||||||
|
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
} else if (op == UserOperation.SaveComment) {
|
||||||
else if (op == UserOperation.CreateCommentLike) {
|
let res: CommentResponse = msg;
|
||||||
|
let found = this.state.comments.find(c => c.id == res.comment.id);
|
||||||
|
found.saved = res.comment.saved;
|
||||||
|
this.setState(this.state);
|
||||||
|
} else if (op == UserOperation.CreateCommentLike) {
|
||||||
let res: CommentResponse = msg;
|
let res: CommentResponse = msg;
|
||||||
let found: Comment = this.state.comments.find(c => c.id === res.comment.id);
|
let found: Comment = this.state.comments.find(c => c.id === res.comment.id);
|
||||||
found.score = res.comment.score;
|
found.score = res.comment.score;
|
||||||
|
@ -243,6 +262,10 @@ export class Post extends Component<any, PostState> {
|
||||||
let res: PostResponse = msg;
|
let res: PostResponse = msg;
|
||||||
this.state.post = res.post;
|
this.state.post = res.post;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
|
} else if (op == UserOperation.SavePost) {
|
||||||
|
let res: PostResponse = msg;
|
||||||
|
this.state.post = res.post;
|
||||||
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.EditCommunity) {
|
} else if (op == UserOperation.EditCommunity) {
|
||||||
let res: CommunityResponse = msg;
|
let res: CommunityResponse = msg;
|
||||||
this.state.community = res.community;
|
this.state.community = res.community;
|
||||||
|
@ -257,12 +280,21 @@ export class Post extends Component<any, PostState> {
|
||||||
} else if (op == UserOperation.BanFromCommunity) {
|
} else if (op == UserOperation.BanFromCommunity) {
|
||||||
let res: BanFromCommunityResponse = msg;
|
let res: BanFromCommunityResponse = msg;
|
||||||
this.state.comments.filter(c => c.creator_id == res.user.id)
|
this.state.comments.filter(c => c.creator_id == res.user.id)
|
||||||
.forEach(c => c.banned = res.banned);
|
.forEach(c => c.banned_from_community = res.banned);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.AddModToCommunity) {
|
} else if (op == UserOperation.AddModToCommunity) {
|
||||||
let res: AddModToCommunityResponse = msg;
|
let res: AddModToCommunityResponse = msg;
|
||||||
this.state.moderators = res.moderators;
|
this.state.moderators = res.moderators;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
|
} else if (op == UserOperation.BanUser) {
|
||||||
|
let res: BanUserResponse = msg;
|
||||||
|
this.state.comments.filter(c => c.creator_id == res.user.id)
|
||||||
|
.forEach(c => c.banned = res.banned);
|
||||||
|
this.setState(this.state);
|
||||||
|
} else if (op == UserOperation.AddAdmin) {
|
||||||
|
let res: AddAdminResponse = msg;
|
||||||
|
this.state.admins = res.admins;
|
||||||
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ export class Setup extends Component<any, State> {
|
||||||
registerUser() {
|
registerUser() {
|
||||||
return (
|
return (
|
||||||
<form onSubmit={linkEvent(this, this.handleRegisterSubmit)}>
|
<form onSubmit={linkEvent(this, this.handleRegisterSubmit)}>
|
||||||
<h4>Set up Site Administrator</h4>
|
<h5>Set up Site Administrator</h5>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label class="col-sm-2 col-form-label">Username</label>
|
<label class="col-sm-2 col-form-label">Username</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
|
|
|
@ -48,11 +48,11 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
let community = this.props.community;
|
let community = this.props.community;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h4 className="mb-0">{community.title}
|
<h5 className="mb-0">{community.title}
|
||||||
{community.removed &&
|
{community.removed &&
|
||||||
<small className="ml-2 text-muted font-italic">removed</small>
|
<small className="ml-2 text-muted font-italic">removed</small>
|
||||||
}
|
}
|
||||||
</h4>
|
</h5>
|
||||||
<Link className="text-muted" to={`/community/${community.id}`}>/f/{community.name}</Link>
|
<Link className="text-muted" to={`/community/${community.id}`}>/f/{community.name}</Link>
|
||||||
{community.am_mod &&
|
{community.am_mod &&
|
||||||
<ul class="list-inline mb-1 text-muted small font-weight-bold">
|
<ul class="list-inline mb-1 text-muted small font-weight-bold">
|
||||||
|
|
|
@ -33,7 +33,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<form onSubmit={linkEvent(this, this.handleCreateSiteSubmit)}>
|
<form onSubmit={linkEvent(this, this.handleCreateSiteSubmit)}>
|
||||||
<h4>{`${this.props.site ? 'Edit' : 'Name'} your Site`}</h4>
|
<h5>{`${this.props.site ? 'Edit' : 'Name'} your Site`}</h5>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label class="col-12 col-form-label">Name</label>
|
<label class="col-12 col-form-label">Name</label>
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
|
|
|
@ -77,7 +77,7 @@ export class User extends Component<any, UserState> {
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-md-9">
|
<div class="col-12 col-md-9">
|
||||||
<h4>/u/{this.state.user.name}</h4>
|
<h5>/u/{this.state.user.name}</h5>
|
||||||
{this.selects()}
|
{this.selects()}
|
||||||
{this.state.view == View.Overview &&
|
{this.state.view == View.Overview &&
|
||||||
this.overview()
|
this.overview()
|
||||||
|
@ -88,6 +88,9 @@ export class User extends Component<any, UserState> {
|
||||||
{this.state.view == View.Posts &&
|
{this.state.view == View.Posts &&
|
||||||
this.posts()
|
this.posts()
|
||||||
}
|
}
|
||||||
|
{this.state.view == View.Saved &&
|
||||||
|
this.overview()
|
||||||
|
}
|
||||||
{this.paginator()}
|
{this.paginator()}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-3">
|
<div class="col-12 col-md-3">
|
||||||
|
@ -108,7 +111,7 @@ export class User extends Component<any, UserState> {
|
||||||
<option value={View.Overview}>Overview</option>
|
<option value={View.Overview}>Overview</option>
|
||||||
<option value={View.Comments}>Comments</option>
|
<option value={View.Comments}>Comments</option>
|
||||||
<option value={View.Posts}>Posts</option>
|
<option value={View.Posts}>Posts</option>
|
||||||
{/* <option value={View.Saved}>Saved</option> */}
|
<option value={View.Saved}>Saved</option>
|
||||||
</select>
|
</select>
|
||||||
<select value={this.state.sort} onChange={linkEvent(this, this.handleSortChange)} class="custom-select w-auto ml-2">
|
<select value={this.state.sort} onChange={linkEvent(this, this.handleSortChange)} class="custom-select w-auto ml-2">
|
||||||
<option disabled>Sort Type</option>
|
<option disabled>Sort Type</option>
|
||||||
|
@ -178,7 +181,7 @@ export class User extends Component<any, UserState> {
|
||||||
let user = this.state.user;
|
let user = this.state.user;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h4>{user.name}</h4>
|
<h5>{user.name}</h5>
|
||||||
<div>Joined <MomentTime data={user} /></div>
|
<div>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>
|
||||||
|
@ -200,7 +203,7 @@ export class User extends Component<any, UserState> {
|
||||||
<div>
|
<div>
|
||||||
{this.state.moderates.length > 0 &&
|
{this.state.moderates.length > 0 &&
|
||||||
<div>
|
<div>
|
||||||
<h4>Moderates</h4>
|
<h5>Moderates</h5>
|
||||||
<ul class="list-unstyled">
|
<ul class="list-unstyled">
|
||||||
{this.state.moderates.map(community =>
|
{this.state.moderates.map(community =>
|
||||||
<li><Link to={`/community/${community.community_id}`}>{community.community_name}</Link></li>
|
<li><Link to={`/community/${community.community_id}`}>{community.community_name}</Link></li>
|
||||||
|
@ -218,7 +221,7 @@ export class User extends Component<any, UserState> {
|
||||||
{this.state.follows.length > 0 &&
|
{this.state.follows.length > 0 &&
|
||||||
<div>
|
<div>
|
||||||
<hr />
|
<hr />
|
||||||
<h4>Subscribed</h4>
|
<h5>Subscribed</h5>
|
||||||
<ul class="list-unstyled">
|
<ul class="list-unstyled">
|
||||||
{this.state.follows.map(community =>
|
{this.state.follows.map(community =>
|
||||||
<li><Link to={`/community/${community.community_id}`}>{community.community_name}</Link></li>
|
<li><Link to={`/community/${community.community_id}`}>{community.community_name}</Link></li>
|
||||||
|
@ -257,6 +260,7 @@ export class User extends Component<any, UserState> {
|
||||||
let form: GetUserDetailsForm = {
|
let form: GetUserDetailsForm = {
|
||||||
user_id: this.state.user_id,
|
user_id: this.state.user_id,
|
||||||
sort: SortType[this.state.sort],
|
sort: SortType[this.state.sort],
|
||||||
|
saved_only: this.state.view == View.Saved,
|
||||||
page: this.state.page,
|
page: this.state.page,
|
||||||
limit: fetchLimit,
|
limit: fetchLimit,
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,9 +7,7 @@
|
||||||
<link rel="shortcut icon" type="image/svg+xml" href="/static/assets/favicon.svg" />
|
<link rel="shortcut icon" type="image/svg+xml" href="/static/assets/favicon.svg" />
|
||||||
|
|
||||||
<title>Lemmy</title>
|
<title>Lemmy</title>
|
||||||
<link rel="stylesheet" href="https://bootswatch.com/4/darkly/bootstrap.min.css">
|
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/balloon-css/0.5.0/balloon.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/balloon-css/0.5.0/balloon.min.css">
|
||||||
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,400i,700,800" rel="stylesheet">
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/sortable/0.8.0/js/sortable.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/sortable/0.8.0/js/sortable.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,8 @@ import { Modlog } from './components/modlog';
|
||||||
import { Setup } from './components/setup';
|
import { Setup } from './components/setup';
|
||||||
import { Symbols } from './components/symbols';
|
import { Symbols } from './components/symbols';
|
||||||
|
|
||||||
import './main.css';
|
import './css/bootstrap.min.css';
|
||||||
|
import './css/main.css';
|
||||||
|
|
||||||
import { WebSocketService, UserService } from './services';
|
import { WebSocketService, UserService } from './services';
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
export enum UserOperation {
|
export enum UserOperation {
|
||||||
Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, EditCommunity, FollowCommunity, GetFollowedCommunities, GetUserDetails, GetModlog, BanFromCommunity, AddModToCommunity, CreateSite, EditSite, GetSite, AddAdmin, BanUser
|
Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, SaveComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, SavePost, EditCommunity, FollowCommunity, GetFollowedCommunities, GetUserDetails, GetModlog, BanFromCommunity, AddModToCommunity, CreateSite, EditSite, GetSite, AddAdmin, BanUser
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum CommentSortType {
|
export enum CommentSortType {
|
||||||
|
@ -41,65 +41,69 @@ export interface CommunityUser {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Community {
|
export interface Community {
|
||||||
user_id?: number;
|
|
||||||
subscribed?: boolean;
|
|
||||||
am_mod?: boolean;
|
|
||||||
removed?: boolean;
|
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
title: string;
|
title: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
creator_id: number;
|
|
||||||
creator_name: string;
|
|
||||||
category_id: number;
|
category_id: number;
|
||||||
|
creator_id: number;
|
||||||
|
removed: boolean;
|
||||||
|
published: string;
|
||||||
|
updated?: string;
|
||||||
|
creator_name: string;
|
||||||
category_name: string;
|
category_name: string;
|
||||||
number_of_subscribers: number;
|
number_of_subscribers: number;
|
||||||
number_of_posts: number;
|
number_of_posts: number;
|
||||||
number_of_comments: number;
|
number_of_comments: number;
|
||||||
published: string;
|
user_id?: number;
|
||||||
updated?: string;
|
subscribed?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Post {
|
export interface Post {
|
||||||
user_id?: number;
|
|
||||||
my_vote?: number;
|
|
||||||
am_mod?: boolean;
|
|
||||||
removed?: boolean;
|
|
||||||
locked?: boolean;
|
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
url?: string;
|
url?: string;
|
||||||
body?: string;
|
body?: string;
|
||||||
creator_id: number;
|
creator_id: number;
|
||||||
creator_name: string;
|
|
||||||
community_id: number;
|
community_id: number;
|
||||||
|
removed: boolean;
|
||||||
|
locked: boolean;
|
||||||
|
published: string;
|
||||||
|
updated?: string;
|
||||||
|
creator_name: string;
|
||||||
community_name: string;
|
community_name: string;
|
||||||
number_of_comments: number;
|
number_of_comments: number;
|
||||||
score: number;
|
score: number;
|
||||||
upvotes: number;
|
upvotes: number;
|
||||||
downvotes: number;
|
downvotes: number;
|
||||||
hot_rank: number;
|
hot_rank: number;
|
||||||
published: string;
|
user_id?: number;
|
||||||
updated?: string;
|
my_vote?: number;
|
||||||
|
subscribed?: boolean;
|
||||||
|
read?: boolean;
|
||||||
|
saved?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Comment {
|
export interface Comment {
|
||||||
id: number;
|
id: number;
|
||||||
content: string;
|
|
||||||
creator_id: number;
|
creator_id: number;
|
||||||
creator_name: string;
|
|
||||||
post_id: number,
|
post_id: number,
|
||||||
community_id: number,
|
|
||||||
parent_id?: number;
|
parent_id?: number;
|
||||||
|
content: string;
|
||||||
|
removed: boolean;
|
||||||
|
read: boolean;
|
||||||
published: string;
|
published: string;
|
||||||
updated?: string;
|
updated?: string;
|
||||||
|
community_id: number,
|
||||||
|
banned: boolean;
|
||||||
|
banned_from_community: boolean;
|
||||||
|
creator_name: string;
|
||||||
score: number;
|
score: number;
|
||||||
upvotes: number;
|
upvotes: number;
|
||||||
downvotes: number;
|
downvotes: number;
|
||||||
|
user_id?: number;
|
||||||
my_vote?: number;
|
my_vote?: number;
|
||||||
am_mod?: boolean;
|
saved?: boolean;
|
||||||
removed?: boolean;
|
|
||||||
banned?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Category {
|
export interface Category {
|
||||||
|
@ -137,7 +141,7 @@ export interface GetUserDetailsForm {
|
||||||
page?: number;
|
page?: number;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
community_id?: number;
|
community_id?: number;
|
||||||
auth?: string;
|
saved_only: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserDetailsResponse {
|
export interface UserDetailsResponse {
|
||||||
|
@ -147,7 +151,6 @@ export interface UserDetailsResponse {
|
||||||
moderates: Array<CommunityUser>;
|
moderates: Array<CommunityUser>;
|
||||||
comments: Array<Comment>;
|
comments: Array<Comment>;
|
||||||
posts: Array<Post>;
|
posts: Array<Post>;
|
||||||
saved?: Array<Post>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BanFromCommunityForm {
|
export interface BanFromCommunityForm {
|
||||||
|
@ -324,7 +327,7 @@ export interface CommunityForm {
|
||||||
description?: string,
|
description?: string,
|
||||||
category_id: number,
|
category_id: number,
|
||||||
edit_id?: number;
|
edit_id?: number;
|
||||||
removed?: boolean;
|
removed: boolean;
|
||||||
reason?: string;
|
reason?: string;
|
||||||
expires?: number;
|
expires?: number;
|
||||||
auth?: string;
|
auth?: string;
|
||||||
|
@ -367,9 +370,9 @@ export interface PostForm {
|
||||||
updated?: number;
|
updated?: number;
|
||||||
edit_id?: number;
|
edit_id?: number;
|
||||||
creator_id: number;
|
creator_id: number;
|
||||||
removed?: boolean;
|
removed: boolean;
|
||||||
|
locked: boolean;
|
||||||
reason?: string;
|
reason?: string;
|
||||||
locked?: boolean;
|
|
||||||
auth: string;
|
auth: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,6 +382,13 @@ export interface GetPostResponse {
|
||||||
comments: Array<Comment>;
|
comments: Array<Comment>;
|
||||||
community: Community;
|
community: Community;
|
||||||
moderators: Array<CommunityUser>;
|
moderators: Array<CommunityUser>;
|
||||||
|
admins: Array<UserView>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SavePostForm {
|
||||||
|
post_id: number;
|
||||||
|
save: boolean;
|
||||||
|
auth?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PostResponse {
|
export interface PostResponse {
|
||||||
|
@ -392,11 +402,17 @@ export interface CommentForm {
|
||||||
parent_id?: number;
|
parent_id?: number;
|
||||||
edit_id?: number;
|
edit_id?: number;
|
||||||
creator_id: number;
|
creator_id: number;
|
||||||
removed?: boolean;
|
removed: boolean;
|
||||||
reason?: string;
|
reason?: string;
|
||||||
auth: string;
|
auth: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SaveCommentForm {
|
||||||
|
comment_id: number;
|
||||||
|
save: boolean;
|
||||||
|
auth?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface CommentResponse {
|
export interface CommentResponse {
|
||||||
op: string;
|
op: string;
|
||||||
comment: Comment;
|
comment: Comment;
|
||||||
|
|
|
@ -1,87 +0,0 @@
|
||||||
body {
|
|
||||||
font-family: 'Open Sans', sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pointer {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-click {
|
|
||||||
pointer-events:none;
|
|
||||||
opacity: 0.65;
|
|
||||||
}
|
|
||||||
|
|
||||||
.upvote:hover {
|
|
||||||
color: var(--info);
|
|
||||||
}
|
|
||||||
|
|
||||||
.downvote:hover {
|
|
||||||
color: var(--danger);
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-control, .form-control:focus {
|
|
||||||
background-color: var(--secondary);
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-control:disabled {
|
|
||||||
background-color: var(--secondary);
|
|
||||||
opacity: .5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-select {
|
|
||||||
color: #fff;
|
|
||||||
background-color: var(--secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.mark {
|
|
||||||
background-color: #322a00;
|
|
||||||
}
|
|
||||||
|
|
||||||
.md-div p {
|
|
||||||
margin-bottom: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.md-div img {
|
|
||||||
max-width: 100%;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.listing {
|
|
||||||
min-height: 61px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
display: inline-flex;
|
|
||||||
width: 1em;
|
|
||||||
height: 1em;
|
|
||||||
stroke-width: 0;
|
|
||||||
stroke: currentColor;
|
|
||||||
fill: currentColor;
|
|
||||||
vertical-align: middle;
|
|
||||||
align-self: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.spin {
|
|
||||||
animation: spins 2s linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes spins {
|
|
||||||
0% { transform: rotate(0deg); }
|
|
||||||
100% { transform: rotate(359deg); }
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-menu {
|
|
||||||
z-index: 2000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-bg {
|
|
||||||
background-color: #222;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote {
|
|
||||||
border-left: 3px solid #ccc;
|
|
||||||
margin: 0.5em 5px;
|
|
||||||
padding: 0.1em 5px;
|
|
||||||
}
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { wsUri } from '../env';
|
import { wsUri } from '../env';
|
||||||
import { LoginForm, RegisterForm, UserOperation, CommunityForm, PostForm, CommentForm, CommentLikeForm, GetPostsForm, CreatePostLikeForm, FollowCommunityForm, GetUserDetailsForm, ListCommunitiesForm, GetModlogForm, BanFromCommunityForm, AddModToCommunityForm, SiteForm, Site, UserView } 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 } 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';
|
||||||
|
@ -96,6 +96,11 @@ export class WebSocketService {
|
||||||
this.subject.next(this.wsSendWrapper(UserOperation.CreateCommentLike, form));
|
this.subject.next(this.wsSendWrapper(UserOperation.CreateCommentLike, form));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public saveComment(form: SaveCommentForm) {
|
||||||
|
this.setAuth(form);
|
||||||
|
this.subject.next(this.wsSendWrapper(UserOperation.SaveComment, form));
|
||||||
|
}
|
||||||
|
|
||||||
public getPosts(form: GetPostsForm) {
|
public getPosts(form: GetPostsForm) {
|
||||||
this.setAuth(form, false);
|
this.setAuth(form, false);
|
||||||
this.subject.next(this.wsSendWrapper(UserOperation.GetPosts, form));
|
this.subject.next(this.wsSendWrapper(UserOperation.GetPosts, form));
|
||||||
|
@ -111,6 +116,11 @@ export class WebSocketService {
|
||||||
this.subject.next(this.wsSendWrapper(UserOperation.EditPost, postForm));
|
this.subject.next(this.wsSendWrapper(UserOperation.EditPost, postForm));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public savePost(form: SavePostForm) {
|
||||||
|
this.setAuth(form);
|
||||||
|
this.subject.next(this.wsSendWrapper(UserOperation.SavePost, form));
|
||||||
|
}
|
||||||
|
|
||||||
public banFromCommunity(form: BanFromCommunityForm) {
|
public banFromCommunity(form: BanFromCommunityForm) {
|
||||||
this.setAuth(form);
|
this.setAuth(form);
|
||||||
this.subject.next(this.wsSendWrapper(UserOperation.BanFromCommunity, form));
|
this.subject.next(this.wsSendWrapper(UserOperation.BanFromCommunity, form));
|
||||||
|
@ -121,8 +131,17 @@ export class WebSocketService {
|
||||||
this.subject.next(this.wsSendWrapper(UserOperation.AddModToCommunity, form));
|
this.subject.next(this.wsSendWrapper(UserOperation.AddModToCommunity, form));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public banUser(form: BanUserForm) {
|
||||||
|
this.setAuth(form);
|
||||||
|
this.subject.next(this.wsSendWrapper(UserOperation.BanUser, form));
|
||||||
|
}
|
||||||
|
|
||||||
|
public addAdmin(form: AddAdminForm) {
|
||||||
|
this.setAuth(form);
|
||||||
|
this.subject.next(this.wsSendWrapper(UserOperation.AddAdmin, form));
|
||||||
|
}
|
||||||
|
|
||||||
public getUserDetails(form: GetUserDetailsForm) {
|
public getUserDetails(form: GetUserDetailsForm) {
|
||||||
this.setAuth(form, false);
|
|
||||||
this.subject.next(this.wsSendWrapper(UserOperation.GetUserDetails, form));
|
this.subject.next(this.wsSendWrapper(UserOperation.GetUserDetails, form));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { UserOperation, Comment } from './interfaces';
|
import { UserOperation, Comment, User } from './interfaces';
|
||||||
import * as markdown_it from 'markdown-it';
|
import * as markdown_it from 'markdown-it';
|
||||||
|
|
||||||
export let repoUrl = 'https://github.com/dessalines/lemmy';
|
export let repoUrl = 'https://github.com/dessalines/lemmy';
|
||||||
|
@ -40,4 +40,23 @@ export function addTypeInfo<T>(arr: Array<T>, name: string): Array<{type_: strin
|
||||||
return arr.map(e => {return {type_: name, data: e}});
|
return arr.map(e => {return {type_: name, data: e}});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function canMod(user: User, modIds: Array<number>, creator_id: number): boolean {
|
||||||
|
// You can do moderator actions only on the mods added after you.
|
||||||
|
if (user) {
|
||||||
|
let yourIndex = modIds.findIndex(id => id == user.id);
|
||||||
|
if (yourIndex == -1) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
modIds = modIds.slice(0, yourIndex+1); // +1 cause you cant mod yourself
|
||||||
|
return !modIds.includes(creator_id);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isMod(modIds: Array<number>, creator_id: number): boolean {
|
||||||
|
return modIds.includes(creator_id);
|
||||||
|
}
|
||||||
|
|
||||||
export let fetchLimit: number = 20;
|
export let fetchLimit: number = 20;
|
||||||
|
|
Loading…
Reference in a new issue