Browse Source

Saving replies, the actual fixes will be in the merge to dev.

online_users
Dessalines 3 years ago
parent
commit
9afadfb9c4
  1. 2
      server/migrations/2019-02-27-170003_create_community/up.sql
  2. 2
      server/migrations/2019-03-03-163336_create_post/down.sql
  3. 19
      server/migrations/2019-03-03-163336_create_post/up.sql
  4. 1
      server/migrations/2019-03-05-233828_create_comment/down.sql
  5. 11
      server/migrations/2019-03-05-233828_create_comment/up.sql
  6. 6
      server/migrations/2019-03-30-212058_create_post_view/up.sql
  7. 7
      server/migrations/2019-04-03-155205_create_community_view/up.sql
  8. 7
      server/migrations/2019-04-03-155309_create_comment_view/up.sql
  9. 4
      server/migrations/2019-04-11-144915_create_mod_views/up.sql
  10. 110
      server/src/actions/comment.rs
  11. 47
      server/src/actions/comment_view.rs
  12. 8
      server/src/actions/community.rs
  13. 6
      server/src/actions/community_view.rs
  14. 6
      server/src/actions/moderator.rs
  15. 163
      server/src/actions/post.rs
  16. 49
      server/src/actions/post_view.rs
  17. 10
      server/src/lib.rs
  18. 45
      server/src/schema.rs
  19. 293
      server/src/websocket_server/server.rs
  20. 229
      ui/src/components/comment-node.tsx
  21. 7
      ui/src/components/comment-nodes.tsx
  22. 4
      ui/src/components/communities.tsx
  23. 6
      ui/src/components/community.tsx
  24. 2
      ui/src/components/create-community.tsx
  25. 2
      ui/src/components/create-post.tsx
  26. 4
      ui/src/components/login.tsx
  27. 12
      ui/src/components/main.tsx
  28. 40
      ui/src/components/modlog.tsx
  29. 26
      ui/src/components/navbar.tsx
  30. 35
      ui/src/components/post-listing.tsx
  31. 2
      ui/src/components/post-listings.tsx
  32. 50
      ui/src/components/post.tsx
  33. 2
      ui/src/components/setup.tsx
  34. 4
      ui/src/components/sidebar.tsx
  35. 2
      ui/src/components/site-form.tsx
  36. 14
      ui/src/components/user.tsx
  37. 2
      ui/src/index.html
  38. 3
      ui/src/index.tsx
  39. 72
      ui/src/interfaces.ts
  40. 87
      ui/src/main.css
  41. 23
      ui/src/services/WebSocketService.ts
  42. 21
      ui/src/utils.ts

2
server/migrations/2019-02-27-170003_create_community/up.sql

@ -38,7 +38,7 @@ create table community (
description text,
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,
removed boolean default false,
removed boolean default false not null,
published timestamp not null default now(),
updated timestamp
);

2
server/migrations/2019-03-03-163336_create_post/down.sql

@ -1,2 +1,4 @@
drop table post_read;
drop table post_saved;
drop table post_like;
drop table post;

19
server/migrations/2019-03-03-163336_create_post/up.sql

@ -5,8 +5,8 @@ create table post (
body text,
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,
removed boolean default false,
locked boolean default false,
removed boolean default false not null,
locked boolean default false not null,
published timestamp not null default now(),
updated timestamp
);
@ -20,3 +20,18 @@ create table post_like (
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
server/migrations/2019-03-05-233828_create_comment/down.sql

@ -1,2 +1,3 @@
drop table comment_saved;
drop table comment_like;
drop table comment;

11
server/migrations/2019-03-05-233828_create_comment/up.sql

@ -4,7 +4,8 @@ create table comment (
post_id int references post on update cascade on delete cascade not null,
parent_id int references comment on update cascade on delete cascade,
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(),
updated timestamp
);
@ -18,3 +19,11 @@ create table comment_like (
published timestamp not null default now(),
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)
);

6
server/migrations/2019-03-30-212058_create_post_view/up.sql

@ -31,7 +31,8 @@ ap.*,
u.id as user_id,
coalesce(pl.score, 0) as my_vote,
(select cf.id::bool from community_follower cf where u.id = cf.user_id and cf.community_id = ap.community_id) as subscribed,
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
cross join all_post ap
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 my_vote,
null as subscribed,
null as am_mod
null as read,
null as saved
from all_post ap
;

7
server/migrations/2019-04-03-155205_create_community_view/up.sql

@ -13,19 +13,16 @@ with all_community as
select
ac.*,
u.id as user_id,
cf.id::boolean 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
(select cf.id::boolean from community_follower cf where u.id = cf.user_id and ac.id = cf.community_id) as subscribed
from user_ u
cross join all_community ac
left join community_follower cf on u.id = cf.user_id and ac.id = cf.community_id
union all
select
ac.*,
null as user_id,
null as subscribed,
null as am_mod
null as subscribed
from all_community ac
;

7
server/migrations/2019-04-03-155309_create_comment_view/up.sql

@ -4,7 +4,8 @@ with all_comment as
select
c.*,
(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,
coalesce(sum(cl.score), 0) as score,
count (case when cl.score = 1 then 1 else null end) as upvotes,
@ -18,7 +19,7 @@ select
ac.*,
u.id as user_id,
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
cross join all_comment ac
left join comment_like cl on u.id = cl.user_id and ac.id = cl.comment_id
@ -29,6 +30,6 @@ select
ac.*,
null as user_id,
null as my_vote,
null as am_mod
null as saved
from all_comment ac
;

4
server/migrations/2019-04-11-144915_create_mod_views/up.sql

@ -43,8 +43,7 @@ create view mod_ban_view as
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.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
select ma.*,
@ -53,7 +52,6 @@ select ma.*,
(select name from community c where ma.community_id = c.id) as community_name
from mod_add_community ma;
create view mod_add_view as
select ma.*,
(select name from user_ u where ma.mod_user_id = u.id) as mod_user_name,

110
server/src/actions/comment.rs

@ -1,9 +1,9 @@
extern crate diesel;
use schema::{comment, comment_like};
use schema::{comment, comment_like, comment_saved};
use diesel::*;
use diesel::result::Error;
use serde::{Deserialize, Serialize};
use {Crud, Likeable};
use {Crud, Likeable, Saveable};
use actions::post::Post;
// WITH RECURSIVE MyTree AS (
@ -22,7 +22,8 @@ pub struct Comment {
pub post_id: i32,
pub parent_id: Option<i32>,
pub content: String,
pub removed: Option<bool>,
pub removed: bool,
pub read: bool,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>
}
@ -38,27 +39,6 @@ pub struct CommentForm {
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 {
fn read(conn: &PgConnection, comment_id: i32) -> Result<Self, Error> {
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 {
fn read(conn: &PgConnection, comment_id_from: i32) -> Result<Vec<Self>, Error> {
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)]
mod tests {
use establish_connection;
@ -150,7 +184,7 @@ mod tests {
description: None,
category_id: 1,
creator_id: inserted_user.id,
removed: None,
removed: false,
updated: None
};
@ -162,8 +196,8 @@ mod tests {
url: None,
body: None,
community_id: inserted_community.id,
removed: None,
locked: None,
removed: false,
locked: false,
updated: None
};
@ -185,7 +219,8 @@ mod tests {
content: "A test comment".into(),
creator_id: inserted_user.id,
post_id: inserted_post.id,
removed: Some(false),
removed: false,
read: false,
parent_id: None,
published: inserted_comment.published,
updated: None
@ -202,6 +237,7 @@ mod tests {
let inserted_child_comment = Comment::create(&conn, &child_comment_form).unwrap();
// Comment Like
let comment_like_form = CommentLikeForm {
comment_id: inserted_comment.id,
post_id: inserted_post.id,
@ -220,9 +256,25 @@ mod tests {
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 updated_comment = Comment::update(&conn, inserted_comment.id, &comment_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();
Comment::delete(&conn, inserted_child_comment.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, updated_comment);
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!(1, like_removed);
assert_eq!(1, saved_removed);
assert_eq!(1, num_deleted);
}

47
server/src/actions/comment_view.rs

@ -13,18 +13,20 @@ table! {
post_id -> Int4,
parent_id -> Nullable<Int4>,
content -> Text,
removed -> Nullable<Bool>,
removed -> Bool,
read -> Bool,
published -> Timestamp,
updated -> Nullable<Timestamp>,
community_id -> Int4,
banned -> Nullable<Bool>,
banned -> Bool,
banned_from_community -> Bool,
creator_name -> Varchar,
score -> BigInt,
upvotes -> BigInt,
downvotes -> BigInt,
user_id -> 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 parent_id: Option<i32>,
pub content: String,
pub removed: Option<bool>,
pub removed: bool,
pub read: bool,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>,
pub community_id: i32,
pub banned: Option<bool>,
pub banned: bool,
pub banned_from_community: bool,
pub creator_name: String,
pub score: i64,
pub upvotes: i64,
pub downvotes: i64,
pub user_id: Option<i32>,
pub my_vote: Option<i32>,
pub am_mod: Option<bool>,
pub saved: Option<bool>,
}
impl CommentView {
@ -57,6 +61,7 @@ impl CommentView {
for_post_id: Option<i32>,
for_creator_id: Option<i32>,
my_user_id: Option<i32>,
saved_only: bool,
page: Option<i64>,
limit: Option<i64>,
) -> Result<Vec<Self>, Error> {
@ -81,6 +86,10 @@ impl CommentView {
if let Some(for_post_id) = for_post_id {
query = query.filter(post_id.eq(for_post_id));
};
if saved_only {
query = query.filter(saved.eq(true));
}
query = match sort {
// SortType::Hot => query.order_by(hot_rank.desc()),
@ -159,7 +168,7 @@ mod tests {
description: None,
category_id: 1,
creator_id: inserted_user.id,
removed: None,
removed: false,
updated: None
};
@ -171,8 +180,8 @@ mod tests {
url: None,
body: None,
community_id: inserted_community.id,
removed: None,
locked: None,
removed: false,
locked: false,
updated: None
};
@ -205,8 +214,10 @@ mod tests {
post_id: inserted_post.id,
community_id: inserted_community.id,
parent_id: None,
removed: Some(false),
banned: None,
removed: false,
read: false,
banned: false,
banned_from_community: false,
published: inserted_comment.published,
updated: None,
creator_name: inserted_user.name.to_owned(),
@ -215,7 +226,7 @@ mod tests {
upvotes: 1,
user_id: None,
my_vote: None,
am_mod: None,
saved: None,
};
let expected_comment_view_with_user = CommentView {
@ -225,8 +236,10 @@ mod tests {
post_id: inserted_post.id,
community_id: inserted_community.id,
parent_id: None,
removed: Some(false),
banned: None,
removed: false,
read: false,
banned: false,
banned_from_community: false,
published: inserted_comment.published,
updated: None,
creator_name: inserted_user.name.to_owned(),
@ -235,11 +248,11 @@ mod tests {
upvotes: 1,
user_id: Some(inserted_user.id),
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_with_user = CommentView::list(&conn, &SortType::New, Some(inserted_post.id), None, Some(inserted_user.id), 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), false, None, None).unwrap();
let like_removed = CommentLike::remove(&conn, &comment_like_form).unwrap();
let num_deleted = Comment::delete(&conn, inserted_comment.id).unwrap();
Post::delete(&conn, inserted_post.id).unwrap();

8
server/src/actions/community.rs

@ -14,7 +14,7 @@ pub struct Community {
pub description: Option<String>,
pub category_id: i32,
pub creator_id: i32,
pub removed: Option<bool>,
pub removed: bool,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>
}
@ -27,7 +27,7 @@ pub struct CommunityForm {
pub description: Option<String>,
pub category_id: i32,
pub creator_id: i32,
pub removed: Option<bool>,
pub removed: bool,
pub updated: Option<chrono::NaiveDateTime>
}
@ -236,7 +236,7 @@ mod tests {
title: "nada".to_owned(),
description: None,
category_id: 1,
removed: None,
removed: false,
updated: None,
};
@ -249,7 +249,7 @@ mod tests {
title: "nada".to_owned(),
description: None,
category_id: 1,
removed: Some(false),
removed: false,
published: inserted_community.published,
updated: None
};

6
server/src/actions/community_view.rs

@ -12,7 +12,7 @@ table! {
description -> Nullable<Text>,
category_id -> Int4,
creator_id -> Int4,
removed -> Nullable<Bool>,
removed -> Bool,
published -> Timestamp,
updated -> Nullable<Timestamp>,
creator_name -> Varchar,
@ -22,7 +22,6 @@ table! {
number_of_comments -> BigInt,
user_id -> Nullable<Int4>,
subscribed -> Nullable<Bool>,
am_mod -> Nullable<Bool>,
}
}
@ -83,7 +82,7 @@ pub struct CommunityView {
pub description: Option<String>,
pub category_id: i32,
pub creator_id: i32,
pub removed: Option<bool>,
pub removed: bool,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>,
pub creator_name: String,
@ -93,7 +92,6 @@ pub struct CommunityView {
pub number_of_comments: i64,
pub user_id: Option<i32>,
pub subscribed: Option<bool>,
pub am_mod: Option<bool>,
}
impl CommunityView {

6
server/src/actions/moderator.rs

@ -441,7 +441,7 @@ mod tests {
description: None,
category_id: 1,
creator_id: inserted_user.id,
removed: None,
removed: false,
updated: None
};
@ -453,8 +453,8 @@ mod tests {
body: None,
creator_id: inserted_user.id,
community_id: inserted_community.id,
removed: None,
locked: None,
removed: false,
locked: false,
updated: None
};

163
server/src/actions/post.rs

@ -1,9 +1,9 @@
extern crate diesel;
use schema::{post, post_like};
use schema::{post, post_like, post_saved, post_read};
use diesel::*;
use diesel::result::Error;
use serde::{Deserialize, Serialize};
use {Crud, Likeable};
use {Crud, Likeable, Saveable, Readable};
#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
#[table_name="post"]
@ -14,8 +14,8 @@ pub struct Post {
pub body: Option<String>,
pub creator_id: i32,
pub community_id: i32,
pub removed: Option<bool>,
pub locked: Option<bool>,
pub removed: bool,
pub locked: bool,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>
}
@ -28,30 +28,11 @@ pub struct PostForm {
pub body: Option<String>,
pub creator_id: i32,
pub community_id: i32,
pub removed: Option<bool>,
pub locked: Option<bool>,
pub removed: bool,
pub locked: bool,
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 {
fn read(conn: &PgConnection, post_id: i32) -> Result<Self, Error> {
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 {
fn read(conn: &PgConnection, post_id_from: i32) -> Result<Vec<Self>, Error> {
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)]
mod tests {
use establish_connection;
@ -132,7 +198,7 @@ mod tests {
description: None,
category_id: 1,
creator_id: inserted_user.id,
removed: None,
removed: false,
updated: None
};
@ -144,8 +210,8 @@ mod tests {
body: None,
creator_id: inserted_user.id,
community_id: inserted_community.id,
removed: None,
locked: None,
removed: false,
locked: false,
updated: None
};
@ -159,11 +225,12 @@ mod tests {
creator_id: inserted_user.id,
community_id: inserted_community.id,
published: inserted_post.published,
removed: Some(false),
locked: Some(false),
removed: false,
locked: false,
updated: None
};
// Post Like
let post_like_form = PostLikeForm {
post_id: inserted_post.id,
user_id: inserted_user.id,
@ -179,10 +246,42 @@ mod tests {
published: inserted_post_like.published,
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 updated_post = Post::update(&conn, inserted_post.id, &new_post).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();
Community::delete(&conn, inserted_community.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, updated_post);
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, saved_removed);
assert_eq!(1, read_removed);
assert_eq!(1, num_deleted);
}

49
server/src/actions/post_view.rs

@ -19,8 +19,8 @@ table! {
body -> Nullable<Text>,
creator_id -> Int4,
community_id -> Int4,
removed -> Nullable<Bool>,
locked -> Nullable<Bool>,
removed -> Bool,
locked -> Bool,
published -> Timestamp,
updated -> Nullable<Timestamp>,
creator_name -> Varchar,
@ -33,7 +33,8 @@ table! {
user_id -> Nullable<Int4>,
my_vote -> Nullable<Int4>,
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 creator_id: i32,
pub community_id: i32,
pub removed: Option<bool>,
pub locked: Option<bool>,
pub removed: bool,
pub locked: bool,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>,
pub creator_name: String,
@ -61,7 +62,8 @@ pub struct PostView {
pub user_id: Option<i32>,
pub my_vote: Option<i32>,
pub subscribed: Option<bool>,
pub am_mod: Option<bool>,
pub read: Option<bool>,
pub saved: Option<bool>,
}
impl PostView {
@ -71,6 +73,8 @@ impl PostView {
for_community_id: Option<i32>,
for_creator_id: Option<i32>,
my_user_id: Option<i32>,
saved_only: bool,
unread_only: bool,
page: Option<i64>,
limit: Option<i64>,
) -> Result<Vec<Self>, Error> {
@ -88,6 +92,15 @@ impl PostView {
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_ {
PostListingType::Subscribed => {
query = query.filter(subscribed.eq(true));
@ -187,7 +200,7 @@ mod tests {
description: None,
creator_id: inserted_user.id,
category_id: 1,
removed: None,
removed: false,
updated: None
};
@ -199,8 +212,8 @@ mod tests {
body: None,
creator_id: inserted_user.id,
community_id: inserted_community.id,
removed: None,
locked: None,
removed: false,
locked: false,
updated: None
};
@ -239,8 +252,8 @@ mod tests {
creator_id: inserted_user.id,
creator_name: user_name.to_owned(),
community_id: inserted_community.id,
removed: Some(false),
locked: Some(false),
removed: false,
locked: false,
community_name: community_name.to_owned(),
number_of_comments: 0,
score: 1,
@ -250,7 +263,8 @@ mod tests {
published: inserted_post.published,
updated: None,
subscribed: None,
am_mod: None,
read: None,
saved: None,
};
let expected_post_listing_with_user = PostView {
@ -260,8 +274,8 @@ mod tests {
name: post_name.to_owned(),
url: None,
body: None,
removed: Some(false),
locked: Some(false),
removed: false,
locked: false,
creator_id: inserted_user.id,
creator_name: user_name.to_owned(),
community_id: inserted_community.id,
@ -274,12 +288,13 @@ mod tests {
published: inserted_post.published,
updated: 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_no_user = PostView::list(&conn, PostListingType::Community, &SortType::New, Some(inserted_community.id), None, None, 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, false, false, None, 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();

10
server/src/lib.rs

@ -55,6 +55,16 @@ pub trait Bannable<T> {
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 {
let db_url = Settings::get().db_url;
PgConnection::establish(&db_url)

45
server/src/schema.rs

@ -12,7 +12,8 @@ table! {
post_id -> Int4,
parent_id -> Nullable<Int4>,
content -> Text,
removed -> Nullable<Bool>,
removed -> Bool,
read -> Bool,
published -> Timestamp,
updated -> Nullable<Timestamp>,
}
@ -29,6 +30,15 @@ table! {
}
}
table! {
comment_saved (id) {
id -> Int4,
comment_id -> Int4,
user_id -> Int4,
published -> Timestamp,
}
}
table! {
community (id) {
id -> Int4,
@ -37,7 +47,7 @@ table! {
description -> Nullable<Text>,
category_id -> Int4,
creator_id -> Int4,
removed -> Nullable<Bool>,
removed -> Bool,
published -> Timestamp,
updated -> Nullable<Timestamp>,
}
@ -168,8 +178,8 @@ table! {
body -> Nullable<Text>,
creator_id -> Int4,
community_id -> Int4,
removed -> Nullable<Bool>,
locked -> Nullable<Bool>,
removed -> Bool,
locked -> Bool,
published -> 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! {
site (id) {
id -> Int4,
@ -225,6 +253,8 @@ joinable!(comment -> user_ (creator_id));
joinable!(comment_like -> comment (comment_id));
joinable!(comment_like -> post (post_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 -> user_ (creator_id));
joinable!(community_follower -> community (community_id));
@ -247,6 +277,10 @@ joinable!(post -> community (community_id));
joinable!(post -> user_ (creator_id));
joinable!(post_like -> post (post_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!(user_ban -> user_ (user_id));
@ -254,6 +288,7 @@ allow_tables_to_appear_in_same_query!(
category,
comment,
comment_like,
comment_saved,
community,
community_follower,
community_moderator,
@ -268,6 +303,8 @@ allow_tables_to_appear_in_same_query!(
mod_remove_post,
post,
post_like,
post_read,
post_saved,
site,
user_,
user_ban,

293
server/src/websocket_server/server.rs

@ -11,7 +11,7 @@ use bcrypt::{verify};
use std::str::FromStr;
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::user::*;
use actions::post::*;
@ -26,7 +26,7 @@ use actions::moderator::*;
#[derive(EnumString,ToString,Debug)]
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)]
@ -164,7 +164,8 @@ pub struct GetPostResponse {
post: PostView,
comments: Vec<CommentView>,
community: CommunityView,
moderators: Vec<CommunityModeratorView>
moderators: Vec<CommunityModeratorView>,
admins: Vec<UserView>,
}
#[derive(Serialize, Deserialize)]
@ -217,6 +218,13 @@ pub struct EditComment {
auth: String
}
#[derive(Serialize, Deserialize)]
pub struct SaveComment {
comment_id: i32,
save: bool,
auth: String
}
#[derive(Serialize, Deserialize)]
pub struct CommentResponse {
op: String,
@ -253,9 +261,16 @@ pub struct EditPost {
name: String,
url: Option<String>,
body: Option<String>,
removed: Option<bool>,
removed: bool,
locked: bool,
reason: Option<String>,
locked: Option<bool>,
auth: String
}
#[derive(Serialize, Deserialize)]
pub struct SavePost {
post_id: i32,
save: bool,
auth: String
}
@ -266,7 +281,7 @@ pub struct EditCommunity {
title: String,
description: Option<String>,
category_id: i32,
removed: Option<bool>,
removed: bool,
reason: Option<String>,
expires: Option<i64>,
auth: String
@ -297,7 +312,7 @@ pub struct GetUserDetails {
page: Option<i64>,
limit: Option<i64>,
community_id: Option<i32>,
auth: Option<String>
saved_only: bool,
}
#[derive(Serialize, Deserialize)]
@ -308,8 +323,6 @@ pub struct GetUserDetailsResponse {
moderates: Vec<CommunityModeratorView>,
comments: Vec<CommentView>,
posts: Vec<PostView>,
saved_posts: Vec<PostView>,
saved_comments: Vec<CommentView>,
}
#[derive(Serialize, Deserialize)]
@ -468,6 +481,8 @@ impl ChatServer {
Some(community_id),
None,
None,
false,
false,
None,
Some(9999))
.unwrap();
@ -491,7 +506,6 @@ impl Handler<Connect> for ChatServer {
type Result = usize;
fn handle(&mut self, msg: Connect, _: &mut Context<Self>) -> Self::Result {
println!("Someone joined");
// notify all users in same room
// self.send_room_message(&"Main".to_owned(), "Someone joined", 0);
@ -513,7 +527,6 @@ impl Handler<Disconnect> for ChatServer {
type Result = ();
fn handle(&mut self, msg: Disconnect, _: &mut Context<Self>) {
println!("Someone disconnected");
// 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();
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 => {
let create_comment_like: CreateCommentLike = serde_json::from_str(data).unwrap();
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();
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 => {
let edit_community: EditCommunity = serde_json::from_str(data).unwrap();
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 {
let community_moderator_form = CommunityModeratorForm {
community_id: 1,
user_id: inserted_user.id
user_id: inserted_user.id,
};
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.");
}
};
let community_follower_form = CommunityFollowerForm {