--- /dev/null
+drop view reply_view;
+drop view comment_view;
+drop view community_view;
+drop view post_view;
+alter table community drop column deleted;
+alter table post drop column deleted;
+alter table comment drop column deleted;
+
+create view community_view as
+with all_community as
+(
+ select *,
+ (select name from user_ u where c.creator_id = u.id) as creator_name,
+ (select name from category ct where c.category_id = ct.id) as category_name,
+ (select count(*) from community_follower cf where cf.community_id = c.id) as number_of_subscribers,
+ (select count(*) from post p where p.community_id = c.id) as number_of_posts,
+ (select count(*) from comment co, post p where c.id = p.community_id and p.id = co.post_id) as number_of_comments
+ from community c
+)
+
+select
+ac.*,
+u.id as user_id,
+(select cf.id::boolean from community_follower cf where u.id = cf.user_id and ac.id = cf.community_id) as subscribed
+from user_ u
+cross join all_community ac
+
+union all
+
+select
+ac.*,
+null as user_id,
+null as subscribed
+from all_community ac
+;
+
+create or replace view post_view as
+with all_post as
+(
+ select
+ p.*,
+ (select name from user_ where p.creator_id = user_.id) as creator_name,
+ (select name from community where p.community_id = community.id) as community_name,
+ (select removed from community c where p.community_id = c.id) as community_removed,
+ (select count(*) from comment where comment.post_id = p.id) as number_of_comments,
+ coalesce(sum(pl.score), 0) as score,
+ count (case when pl.score = 1 then 1 else null end) as upvotes,
+ count (case when pl.score = -1 then 1 else null end) as downvotes,
+ hot_rank(coalesce(sum(pl.score) , 0), p.published) as hot_rank
+ from post p
+ left join post_like pl on p.id = pl.post_id
+ group by p.id
+)
+
+select
+ap.*,
+u.id as user_id,
+coalesce(pl.score, 0) as my_vote,
+(select cf.id::bool from community_follower cf where u.id = cf.user_id and cf.community_id = ap.community_id) as subscribed,
+(select pr.id::bool from post_read pr where u.id = pr.user_id and pr.post_id = ap.id) as read,
+(select ps.id::bool from post_saved ps where u.id = ps.user_id and ps.post_id = ap.id) as saved
+from user_ u
+cross join all_post ap
+left join post_like pl on u.id = pl.user_id and ap.id = pl.post_id
+
+union all
+
+select
+ap.*,
+null as user_id,
+null as my_vote,
+null as subscribed,
+null as read,
+null as saved
+from all_post ap
+;
+
+create view comment_view as
+with all_comment as
+(
+ select
+ c.*,
+ (select community_id from post p where p.id = c.post_id),
+ (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,
+ count (case when cl.score = -1 then 1 else null end) as downvotes
+ from comment c
+ left join comment_like cl on c.id = cl.comment_id
+ group by c.id
+)
+
+select
+ac.*,
+u.id as user_id,
+coalesce(cl.score, 0) as my_vote,
+(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
+
+union all
+
+select
+ ac.*,
+ null as user_id,
+ null as my_vote,
+ null as saved
+from all_comment ac
+;
+
+create view reply_view as
+with closereply as (
+ select
+ c2.id,
+ c2.creator_id as sender_id,
+ c.creator_id as recipient_id
+ from comment c
+ inner join comment c2 on c.id = c2.parent_id
+ where c2.creator_id != c.creator_id
+ -- Do union where post is null
+ union
+ select
+ c.id,
+ c.creator_id as sender_id,
+ p.creator_id as recipient_id
+ from comment c, post p
+ where c.post_id = p.id and c.parent_id is null and c.creator_id != p.creator_id
+)
+select cv.*,
+closereply.recipient_id
+from comment_view cv, closereply
+where closereply.id = cv.id
+;
+
--- /dev/null
+alter table community add column deleted boolean default false not null;
+alter table post add column deleted boolean default false not null;
+alter table comment add column deleted boolean default false not null;
+
+-- The views
+drop view community_view;
+
+create view community_view as
+with all_community as
+(
+ select *,
+ (select name from user_ u where c.creator_id = u.id) as creator_name,
+ (select name from category ct where c.category_id = ct.id) as category_name,
+ (select count(*) from community_follower cf where cf.community_id = c.id) as number_of_subscribers,
+ (select count(*) from post p where p.community_id = c.id) as number_of_posts,
+ (select count(*) from comment co, post p where c.id = p.community_id and p.id = co.post_id) as number_of_comments
+ from community c
+)
+
+select
+ac.*,
+u.id as user_id,
+(select cf.id::boolean from community_follower cf where u.id = cf.user_id and ac.id = cf.community_id) as subscribed
+from user_ u
+cross join all_community ac
+
+union all
+
+select
+ac.*,
+null as user_id,
+null as subscribed
+from all_community ac
+;
+
+
+drop view post_view;
+create view post_view as
+with all_post as
+(
+ select
+ p.*,
+ (select name from user_ where p.creator_id = user_.id) as creator_name,
+ (select name from community where p.community_id = community.id) as community_name,
+ (select removed from community c where p.community_id = c.id) as community_removed,
+ (select deleted from community c where p.community_id = c.id) as community_deleted,
+ (select count(*) from comment where comment.post_id = p.id) as number_of_comments,
+ coalesce(sum(pl.score), 0) as score,
+ count (case when pl.score = 1 then 1 else null end) as upvotes,
+ count (case when pl.score = -1 then 1 else null end) as downvotes,
+ hot_rank(coalesce(sum(pl.score) , 0), p.published) as hot_rank
+ from post p
+ left join post_like pl on p.id = pl.post_id
+ group by p.id
+)
+
+select
+ap.*,
+u.id as user_id,
+coalesce(pl.score, 0) as my_vote,
+(select cf.id::bool from community_follower cf where u.id = cf.user_id and cf.community_id = ap.community_id) as subscribed,
+(select pr.id::bool from post_read pr where u.id = pr.user_id and pr.post_id = ap.id) as read,
+(select ps.id::bool from post_saved ps where u.id = ps.user_id and ps.post_id = ap.id) as saved
+from user_ u
+cross join all_post ap
+left join post_like pl on u.id = pl.user_id and ap.id = pl.post_id
+
+union all
+
+select
+ap.*,
+null as user_id,
+null as my_vote,
+null as subscribed,
+null as read,
+null as saved
+from all_post ap
+;
+
+drop view reply_view;
+drop view comment_view;
+create view comment_view as
+with all_comment as
+(
+ select
+ c.*,
+ (select community_id from post p where p.id = c.post_id),
+ (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,
+ count (case when cl.score = -1 then 1 else null end) as downvotes
+ from comment c
+ left join comment_like cl on c.id = cl.comment_id
+ group by c.id
+)
+
+select
+ac.*,
+u.id as user_id,
+coalesce(cl.score, 0) as my_vote,
+(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
+
+union all
+
+select
+ ac.*,
+ null as user_id,
+ null as my_vote,
+ null as saved
+from all_comment ac
+;
+
+create view reply_view as
+with closereply as (
+ select
+ c2.id,
+ c2.creator_id as sender_id,
+ c.creator_id as recipient_id
+ from comment c
+ inner join comment c2 on c.id = c2.parent_id
+ where c2.creator_id != c.creator_id
+ -- Do union where post is null
+ union
+ select
+ c.id,
+ c.creator_id as sender_id,
+ p.creator_id as recipient_id
+ from comment c, post p
+ where c.post_id = p.id and c.parent_id is null and c.creator_id != p.creator_id
+)
+select cv.*,
+closereply.recipient_id
+from comment_view cv, closereply
+where closereply.id = cv.id
+;
+
pub removed: bool,
pub read: bool,
pub published: chrono::NaiveDateTime,
- pub updated: Option<chrono::NaiveDateTime>
+ pub updated: Option<chrono::NaiveDateTime>,
+ pub deleted: bool,
}
#[derive(Insertable, AsChangeset, Clone)]
pub content: String,
pub removed: Option<bool>,
pub read: Option<bool>,
- pub updated: Option<chrono::NaiveDateTime>
+ pub updated: Option<chrono::NaiveDateTime>,
+ pub deleted: Option<bool>,
}
impl Crud<CommentForm> for Comment {
category_id: 1,
creator_id: inserted_user.id,
removed: None,
+ deleted: None,
updated: None
};
body: None,
community_id: inserted_community.id,
removed: None,
+ deleted: None,
locked: None,
updated: None
};
creator_id: inserted_user.id,
post_id: inserted_post.id,
removed: None,
+ deleted: None,
read: None,
parent_id: None,
updated: None
creator_id: inserted_user.id,
post_id: inserted_post.id,
removed: false,
+ deleted: false,
read: false,
parent_id: None,
published: inserted_comment.published,
post_id: inserted_post.id,
parent_id: Some(inserted_comment.id),
removed: None,
+ deleted: None,
read: None,
updated: None
};
read -> Bool,
published -> Timestamp,
updated -> Nullable<Timestamp>,
+ deleted -> Bool,
community_id -> Int4,
banned -> Bool,
banned_from_community -> Bool,
pub read: bool,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>,
+ pub deleted: bool,
pub community_id: i32,
pub banned: bool,
pub banned_from_community: bool,
_ => query.order_by(published.desc())
};
+ // Note: deleted and removed comments are done on the front side
query
.limit(limit)
.offset(offset)
read -> Bool,
published -> Timestamp,
updated -> Nullable<Timestamp>,
+ deleted -> Bool,
community_id -> Int4,
banned -> Bool,
banned_from_community -> Bool,
pub read: bool,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>,
+ pub deleted: bool,
pub community_id: i32,
pub banned: bool,
pub banned_from_community: bool,
category_id: 1,
creator_id: inserted_user.id,
removed: None,
+ deleted: None,
updated: None
};
body: None,
community_id: inserted_community.id,
removed: None,
+ deleted: None,
locked: None,
updated: None
};
post_id: inserted_post.id,
parent_id: None,
removed: None,
+ deleted: None,
read: None,
updated: None
};
community_id: inserted_community.id,
parent_id: None,
removed: false,
+ deleted: false,
read: false,
banned: false,
banned_from_community: false,
community_id: inserted_community.id,
parent_id: None,
removed: false,
+ deleted: false,
read: false,
banned: false,
banned_from_community: false,
pub creator_id: i32,
pub removed: bool,
pub published: chrono::NaiveDateTime,
- pub updated: Option<chrono::NaiveDateTime>
+ pub updated: Option<chrono::NaiveDateTime>,
+ pub deleted: bool,
}
#[derive(Insertable, AsChangeset, Clone, Serialize, Deserialize)]
pub category_id: i32,
pub creator_id: i32,
pub removed: Option<bool>,
- pub updated: Option<chrono::NaiveDateTime>
+ pub updated: Option<chrono::NaiveDateTime>,
+ pub deleted: Option<bool>,
}
impl Crud<CommunityForm> for Community {
description: None,
category_id: 1,
removed: None,
+ deleted: None,
updated: None,
};
description: None,
category_id: 1,
removed: false,
+ deleted: false,
published: inserted_community.published,
updated: None
};
removed -> Bool,
published -> Timestamp,
updated -> Nullable<Timestamp>,
+ deleted -> Bool,
creator_name -> Varchar,
category_name -> Varchar,
number_of_subscribers -> BigInt,
pub removed: bool,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>,
+ pub deleted: bool,
pub creator_name: String,
pub category_name: String,
pub number_of_subscribers: i64,
.limit(limit)
.offset(offset)
.filter(removed.eq(false))
+ .filter(deleted.eq(false))
.load::<Self>(conn)
}
}
category_id: 1,
creator_id: inserted_user.id,
removed: None,
+ deleted: None,
updated: None
};
creator_id: inserted_user.id,
community_id: inserted_community.id,
removed: None,
+ deleted: None,
locked: None,
updated: None
};
creator_id: inserted_user.id,
post_id: inserted_post.id,
removed: None,
+ deleted: None,
read: None,
parent_id: None,
updated: None
pub removed: bool,
pub locked: bool,
pub published: chrono::NaiveDateTime,
- pub updated: Option<chrono::NaiveDateTime>
+ pub updated: Option<chrono::NaiveDateTime>,
+ pub deleted: bool,
}
#[derive(Insertable, AsChangeset, Clone)]
pub community_id: i32,
pub removed: Option<bool>,
pub locked: Option<bool>,
- pub updated: Option<chrono::NaiveDateTime>
+ pub updated: Option<chrono::NaiveDateTime>,
+ pub deleted: Option<bool>,
}
impl Crud<PostForm> for Post {
category_id: 1,
creator_id: inserted_user.id,
removed: None,
+ deleted: None,
updated: None
};
creator_id: inserted_user.id,
community_id: inserted_community.id,
removed: None,
+ deleted: None,
locked: None,
updated: None
};
published: inserted_post.published,
removed: false,
locked: false,
+ deleted: false,
updated: None
};
locked -> Bool,
published -> Timestamp,
updated -> Nullable<Timestamp>,
+ deleted -> Bool,
creator_name -> Varchar,
community_name -> Varchar,
community_removed -> Bool,
+ community_deleted -> Bool,
number_of_comments -> BigInt,
score -> BigInt,
upvotes -> BigInt,
pub locked: bool,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>,
+ pub deleted: bool,
pub creator_name: String,
pub community_name: String,
pub community_removed: bool,
+ pub community_deleted: bool,
pub number_of_comments: i64,
pub score: i64,
pub upvotes: i64,
.limit(limit)
.offset(offset)
.filter(removed.eq(false))
- .filter(community_removed.eq(false));
+ .filter(deleted.eq(false))
+ .filter(community_removed.eq(false))
+ .filter(community_deleted.eq(false));
query.load::<Self>(conn)
}
creator_id: inserted_user.id,
category_id: 1,
removed: None,
+ deleted: None,
updated: None
};
creator_id: inserted_user.id,
community_id: inserted_community.id,
removed: None,
+ deleted: None,
locked: None,
updated: None
};
creator_name: user_name.to_owned(),
community_id: inserted_community.id,
removed: false,
+ deleted: false,
locked: false,
community_name: community_name.to_owned(),
community_removed: false,
+ community_deleted: false,
number_of_comments: 0,
score: 1,
upvotes: 1,
url: None,
body: None,
removed: false,
+ deleted: false,
locked: false,
creator_id: inserted_user.id,
creator_name: user_name.to_owned(),
community_id: inserted_community.id,
community_name: community_name.to_owned(),
community_removed: false,
+ community_deleted: false,
number_of_comments: 0,
score: 1,
upvotes: 1,
read -> Bool,
published -> Timestamp,
updated -> Nullable<Timestamp>,
+ deleted -> Bool,
}
}
removed -> Bool,
published -> Timestamp,
updated -> Nullable<Timestamp>,
+ deleted -> Bool,
}
}
locked -> Bool,
published -> Timestamp,
updated -> Nullable<Timestamp>,
+ deleted -> Bool,
}
}
creator_id: i32,
post_id: i32,
removed: Option<bool>,
+ deleted: Option<bool>,
reason: Option<String>,
read: Option<bool>,
auth: String
url: Option<String>,
body: Option<String>,
removed: Option<bool>,
+ deleted: Option<bool>,
locked: Option<bool>,
reason: Option<String>,
auth: String
description: Option<String>,
category_id: i32,
removed: Option<bool>,
+ deleted: Option<bool>,
reason: Option<String>,
expires: Option<i64>,
auth: String
category_id: self.category_id,
creator_id: user_id,
removed: None,
+ deleted: None,
updated: None,
};
community_id: self.community_id,
creator_id: user_id,
removed: None,
+ deleted: None,
locked: None,
updated: None
};
post_id: self.post_id,
creator_id: user_id,
removed: None,
+ deleted: None,
read: None,
updated: None
};
post_id: self.post_id,
creator_id: self.creator_id,
removed: self.removed.to_owned(),
+ deleted: self.deleted.to_owned(),
read: self.read.to_owned(),
updated: if self.read.is_some() { orig_comment.updated } else {Some(naive_now())}
};
creator_id: self.creator_id.to_owned(),
community_id: self.community_id,
removed: self.removed.to_owned(),
+ deleted: self.deleted.to_owned(),
locked: self.locked.to_owned(),
updated: Some(naive_now())
};
category_id: self.category_id.to_owned(),
creator_id: user_id,
removed: self.removed.to_owned(),
+ deleted: self.deleted.to_owned(),
updated: Some(naive_now())
};
post_id: reply.to_owned().post_id,
creator_id: reply.to_owned().creator_id,
removed: None,
+ deleted: None,
read: Some(true),
updated: reply.to_owned().updated
};
{this.state.showEdit && <CommentForm node={node} edit onReplyCancel={this.handleReplyCancel} disabled={this.props.locked} />}
{!this.state.showEdit &&
<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.deleted ? '*deleted*' : node.comment.content)} />
<ul class="list-inline mb-1 text-muted small font-weight-bold">
{UserService.Instance.user && !this.props.viewOnly &&
<>
<span class="pointer" onClick={linkEvent(this, this.handleEditClick)}>edit</span>
</li>
<li className="list-inline-item">
- <span class="pointer" onClick={linkEvent(this, this.handleDeleteClick)}>delete</span>
+ <span class="pointer" onClick={linkEvent(this, this.handleDeleteClick)}>
+ {!this.props.node.comment.deleted ? 'delete' : 'restore'}
+ </span>
</li>
</>
}
handleDeleteClick(i: CommentNode) {
let deleteForm: CommentFormI = {
- content: '*deleted*',
+ content: i.props.node.comment.content,
edit_id: i.props.node.comment.id,
creator_id: i.props.node.comment.creator_id,
post_id: i.props.node.comment.post_id,
parent_id: i.props.node.comment.parent_id,
+ deleted: !i.props.node.comment.deleted,
auth: null
};
WebSocketService.Instance.editComment(deleteForm);
found.content = res.comment.content;
found.updated = res.comment.updated;
found.removed = res.comment.removed;
+ found.deleted = res.comment.deleted;
found.upvotes = res.comment.upvotes;
found.downvotes = res.comment.downvotes;
found.score = res.comment.score;
{post.removed &&
<small className="ml-2 text-muted font-italic">removed</small>
}
+ {post.deleted &&
+ <small className="ml-2 text-muted font-italic">deleted</small>
+ }
{post.locked &&
<small className="ml-2 text-muted font-italic">locked</small>
}
{UserService.Instance.user && this.props.editable &&
<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>
+ <span class="pointer" onClick={linkEvent(this, this.handleSavePostClick)}>{post.saved ? 'unsave' : 'save'}</span>
</li>
{this.myPost &&
<>
<span class="pointer" onClick={linkEvent(this, this.handleEditClick)}>edit</span>
</li>
<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)}>
+ {!post.deleted ? 'delete' : 'restore'}
+ </span>
</li>
</>
}
handleDeleteClick(i: PostListing) {
let deleteForm: PostFormI = {
- body: '',
+ body: i.props.post.body,
community_id: i.props.post.community_id,
- name: "deleted",
- url: '',
+ name: i.props.post.name,
+ url: i.props.post.url,
edit_id: i.props.post.id,
creator_id: i.props.post.creator_id,
+ deleted: !i.props.post.deleted,
auth: null
};
WebSocketService.Instance.editPost(deleteForm);
found.content = res.comment.content;
found.updated = res.comment.updated;
found.removed = res.comment.removed;
+ found.deleted = res.comment.deleted;
found.upvotes = res.comment.upvotes;
found.downvotes = res.comment.downvotes;
found.score = res.comment.score;
{community.removed &&
<small className="ml-2 text-muted font-italic">removed</small>
}
+ {community.deleted &&
+ <small className="ml-2 text-muted font-italic">deleted</small>
+ }
</h5>
<Link className="text-muted" to={`/c/${community.name}`}>/c/{community.name}</Link>
<ul class="list-inline mb-1 text-muted small font-weight-bold">
</li>
{this.amCreator &&
<li className="list-inline-item">
- {/* <span class="pointer" onClick={linkEvent(this, this.handleDeleteClick)}>delete</span> */}
+ <span class="pointer" onClick={linkEvent(this, this.handleDeleteClick)}>
+ {!community.deleted ? 'delete' : 'restore'}
+ </span>
</li>
}
</>
this.setState(this.state);
}
- // TODO no deleting communities yet
- // handleDeleteClick(i: Sidebar, event) {
- // }
+ handleDeleteClick(i: Sidebar) {
+ event.preventDefault();
+ let deleteForm: CommunityFormI = {
+ name: i.props.community.name,
+ title: i.props.community.title,
+ category_id: i.props.community.category_id,
+ edit_id: i.props.community.id,
+ deleted: !i.props.community.deleted,
+ auth: null,
+ };
+ WebSocketService.Instance.editCommunity(deleteForm);
+ }
handleUnsubscribe(communityId: number) {
let form: FollowCommunityForm = {
return UserService.Instance.user && this.props.admins.map(a => a.id).includes(UserService.Instance.user.id);
}
- handleDeleteClick() {
- }
-
handleModRemoveShow(i: Sidebar) {
i.state.showRemoveDialog = true;
i.setState(i.state);
found.content = res.comment.content;
found.updated = res.comment.updated;
found.removed = res.comment.removed;
+ found.deleted = res.comment.deleted;
found.upvotes = res.comment.upvotes;
found.downvotes = res.comment.downvotes;
found.score = res.comment.score;
category_id: number;
creator_id: number;
removed: boolean;
+ deleted: boolean;
published: string;
updated?: string;
creator_name: string;
creator_id: number;
community_id: number;
removed: boolean;
+ deleted: boolean;
locked: boolean;
published: string;
updated?: string;
parent_id?: number;
content: string;
removed: boolean;
+ deleted: boolean;
read: boolean;
published: string;
updated?: string;
category_id: number,
edit_id?: number;
removed?: boolean;
+ deleted?: boolean;
reason?: string;
expires?: number;
auth?: string;
edit_id?: number;
creator_id: number;
removed?: boolean;
+ deleted?: boolean;
locked?: boolean;
reason?: string;
auth: string;
edit_id?: number;
creator_id: number;
removed?: boolean;
+ deleted?: boolean;
reason?: string;
read?: boolean;
auth: string;