Added comment delete, remove, read.

pull/997/head
Dessalines 4 years ago
parent ca7d2feedb
commit fd96dfdb5e

@ -1448,7 +1448,6 @@ Mods and admins can remove and lock a post, creators can delete it.
data: { data: {
content: String, content: String,
parent_id: Option<i32>, parent_id: Option<i32>,
edit_id: Option<i32>,
post_id: i32, post_id: i32,
auth: String auth: String
} }
@ -1470,7 +1469,7 @@ Mods and admins can remove and lock a post, creators can delete it.
#### Edit Comment #### Edit Comment
Mods and admins can remove a comment, creators can delete it. Only the creator can edit the comment.
##### Request ##### Request
```rust ```rust
@ -1478,15 +1477,8 @@ Mods and admins can remove a comment, creators can delete it.
op: "EditComment", op: "EditComment",
data: { data: {
content: String, content: String,
parent_id: Option<i32>,
edit_id: i32, edit_id: i32,
creator_id: i32, auth: String,
post_id: i32,
removed: Option<bool>,
deleted: Option<bool>,
reason: Option<String>,
read: Option<bool>,
auth: String
} }
} }
``` ```
@ -1503,6 +1495,89 @@ Mods and admins can remove a comment, creators can delete it.
`PUT /comment` `PUT /comment`
#### Delete Comment
Only the creator can delete the comment.
##### Request
```rust
{
op: "DeleteComment",
data: {
edit_id: i32,
deleted: bool,
auth: String,
}
}
```
##### Response
```rust
{
op: "DeleteComment",
data: {
comment: CommentView
}
}
```
##### HTTP
`POST /comment/delete`
#### Remove Comment
Only a mod or admin can remove the comment.
##### Request
```rust
{
op: "RemoveComment",
data: {
edit_id: i32,
removed: bool,
reason: Option<String>,
auth: String,
}
}
```
##### Response
```rust
{
op: "RemoveComment",
data: {
comment: CommentView
}
}
```
##### HTTP
`POST /comment/remove`
#### Mark Comment as Read
##### Request
```rust
{
op: "MarkCommentAsRead",
data: {
edit_id: i32,
read: bool,
auth: String,
}
}
```
##### Response
```rust
{
op: "MarkCommentAsRead",
data: {
comment: CommentView
}
}
```
##### HTTP
`POST /comment/mark_as_read`
#### Save Comment #### Save Comment
##### Request ##### Request
```rust ```rust
@ -1538,7 +1613,6 @@ Mods and admins can remove a comment, creators can delete it.
op: "CreateCommentLike", op: "CreateCommentLike",
data: { data: {
comment_id: i32, comment_id: i32,
post_id: i32,
score: i16, score: i16,
auth: String auth: String
} }

@ -97,14 +97,6 @@ impl Comment {
comment.filter(ap_id.eq(object_id)).first::<Self>(conn) comment.filter(ap_id.eq(object_id)).first::<Self>(conn)
} }
pub fn mark_as_read(conn: &PgConnection, comment_id: i32) -> Result<Self, Error> {
use crate::schema::comment::dsl::*;
diesel::update(comment.find(comment_id))
.set(read.eq(true))
.get_result::<Self>(conn)
}
pub fn permadelete(conn: &PgConnection, comment_id: i32) -> Result<Self, Error> { pub fn permadelete(conn: &PgConnection, comment_id: i32) -> Result<Self, Error> {
use crate::schema::comment::dsl::*; use crate::schema::comment::dsl::*;
@ -116,6 +108,46 @@ impl Comment {
)) ))
.get_result::<Self>(conn) .get_result::<Self>(conn)
} }
pub fn update_deleted(
conn: &PgConnection,
comment_id: i32,
new_deleted: bool,
) -> Result<Self, Error> {
use crate::schema::comment::dsl::*;
diesel::update(comment.find(comment_id))
.set(deleted.eq(new_deleted))
.get_result::<Self>(conn)
}
pub fn update_removed(
conn: &PgConnection,
comment_id: i32,
new_removed: bool,
) -> Result<Self, Error> {
use crate::schema::comment::dsl::*;
diesel::update(comment.find(comment_id))
.set(removed.eq(new_removed))
.get_result::<Self>(conn)
}
pub fn update_read(conn: &PgConnection, comment_id: i32, new_read: bool) -> Result<Self, Error> {
use crate::schema::comment::dsl::*;
diesel::update(comment.find(comment_id))
.set(read.eq(new_read))
.get_result::<Self>(conn)
}
pub fn update_content(
conn: &PgConnection,
comment_id: i32,
new_content: &str,
) -> Result<Self, Error> {
use crate::schema::comment::dsl::*;
diesel::update(comment.find(comment_id))
.set((content.eq(new_content), updated.eq(naive_now())))
.get_result::<Self>(conn)
}
} }
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug, Clone)] #[derive(Identifiable, Queryable, Associations, PartialEq, Debug, Clone)]

@ -1,4 +1,5 @@
use crate::{ use crate::{
naive_now,
schema::{community, community_follower, community_moderator, community_user_ban}, schema::{community, community_follower, community_moderator, community_user_ban},
Bannable, Bannable,
Crud, Crud,
@ -29,7 +30,6 @@ pub struct Community {
pub last_refreshed_at: chrono::NaiveDateTime, pub last_refreshed_at: chrono::NaiveDateTime,
} }
// TODO add better delete, remove, lock actions here.
#[derive(Insertable, AsChangeset, Clone, Serialize, Deserialize, Debug)] #[derive(Insertable, AsChangeset, Clone, Serialize, Deserialize, Debug)]
#[table_name = "community"] #[table_name = "community"]
pub struct CommunityForm { pub struct CommunityForm {
@ -129,9 +129,24 @@ impl Community {
) -> Result<Self, Error> { ) -> Result<Self, Error> {
use crate::schema::community::dsl::*; use crate::schema::community::dsl::*;
diesel::update(community.find(community_id)) diesel::update(community.find(community_id))
.set(creator_id.eq(new_creator_id)) .set((creator_id.eq(new_creator_id), updated.eq(naive_now())))
.get_result::<Self>(conn) .get_result::<Self>(conn)
} }
pub fn community_mods_and_admins(
conn: &PgConnection,
community_id: i32,
) -> Result<Vec<i32>, Error> {
use crate::{community_view::CommunityModeratorView, user_view::UserView};
let mut mods_and_admins: Vec<i32> = Vec::new();
mods_and_admins.append(
&mut CommunityModeratorView::for_community(conn, community_id)
.map(|v| v.into_iter().map(|m| m.user_id).collect())?,
);
mods_and_admins
.append(&mut UserView::admins(conn).map(|v| v.into_iter().map(|a| a.id).collect())?);
Ok(mods_and_admins)
}
} }
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)] #[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]

@ -1,4 +1,4 @@
use crate::{schema::private_message, Crud}; use crate::{naive_now, schema::private_message, Crud};
use diesel::{dsl::*, result::Error, *}; use diesel::{dsl::*, result::Error, *};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -88,7 +88,7 @@ impl PrivateMessage {
) -> Result<Self, Error> { ) -> Result<Self, Error> {
use crate::schema::private_message::dsl::*; use crate::schema::private_message::dsl::*;
diesel::update(private_message.find(private_message_id)) diesel::update(private_message.find(private_message_id))
.set(content.eq(new_content)) .set((content.eq(new_content), updated.eq(naive_now())))
.get_result::<Self>(conn) .get_result::<Self>(conn)
} }

@ -13,14 +13,13 @@ use crate::{
use lemmy_db::{ use lemmy_db::{
comment::*, comment::*,
comment_view::*, comment_view::*,
community::Community,
community_view::*, community_view::*,
moderator::*, moderator::*,
naive_now,
post::*, post::*,
site_view::*, site_view::*,
user::*, user::*,
user_mention::*, user_mention::*,
user_view::*,
Crud, Crud,
Likeable, Likeable,
ListingType, ListingType,
@ -44,7 +43,6 @@ use std::str::FromStr;
pub struct CreateComment { pub struct CreateComment {
content: String, content: String,
parent_id: Option<i32>, parent_id: Option<i32>,
edit_id: Option<i32>, // TODO this isn't used
pub post_id: i32, pub post_id: i32,
auth: String, auth: String,
} }
@ -52,14 +50,29 @@ pub struct CreateComment {
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct EditComment { pub struct EditComment {
content: String, content: String,
parent_id: Option<i32>, // TODO why are the parent_id, creator_id, post_id, etc fields required? They aren't going to change
edit_id: i32, edit_id: i32,
creator_id: i32, auth: String,
pub post_id: i32, }
removed: Option<bool>,
deleted: Option<bool>, #[derive(Serialize, Deserialize)]
pub struct DeleteComment {
edit_id: i32,
deleted: bool,
auth: String,
}
#[derive(Serialize, Deserialize)]
pub struct RemoveComment {
edit_id: i32,
removed: bool,
reason: Option<String>, reason: Option<String>,
read: Option<bool>, auth: String,
}
#[derive(Serialize, Deserialize)]
pub struct MarkCommentAsRead {
edit_id: i32,
read: bool,
auth: String, auth: String,
} }
@ -79,7 +92,6 @@ pub struct CommentResponse {
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct CreateCommentLike { pub struct CreateCommentLike {
comment_id: i32, comment_id: i32,
pub post_id: i32,
score: i16, score: i16,
auth: String, auth: String,
} }
@ -150,6 +162,7 @@ impl Perform for Oper<CreateComment> {
return Err(APIError::err("site_ban").into()); return Err(APIError::err("site_ban").into());
} }
// Create the comment
let comment_form2 = comment_form.clone(); let comment_form2 = comment_form.clone();
let inserted_comment = let inserted_comment =
match blocking(pool, move |conn| Comment::create(&conn, &comment_form2)).await? { match blocking(pool, move |conn| Comment::create(&conn, &comment_form2)).await? {
@ -157,6 +170,7 @@ impl Perform for Oper<CreateComment> {
Err(_e) => return Err(APIError::err("couldnt_create_comment").into()), Err(_e) => return Err(APIError::err("couldnt_create_comment").into()),
}; };
// Necessary to update the ap_id
let inserted_comment_id = inserted_comment.id; let inserted_comment_id = inserted_comment.id;
let updated_comment: Comment = match blocking(pool, move |conn| { let updated_comment: Comment = match blocking(pool, move |conn| {
let apub_id = let apub_id =
@ -175,8 +189,15 @@ impl Perform for Oper<CreateComment> {
// Scan the comment for user mentions, add those rows // Scan the comment for user mentions, add those rows
let mentions = scrape_text_for_mentions(&comment_form.content); let mentions = scrape_text_for_mentions(&comment_form.content);
let recipient_ids = let recipient_ids = send_local_notifs(
send_local_notifs(mentions, updated_comment.clone(), user.clone(), post, pool).await?; mentions,
updated_comment.clone(),
user.clone(),
post,
pool,
true,
)
.await?;
// You like your own comment by default // You like your own comment by default
let like_form = CommentLikeForm { let like_form = CommentLikeForm {
@ -237,122 +258,127 @@ impl Perform for Oper<EditComment> {
let user_id = claims.id; let user_id = claims.id;
let user = blocking(pool, move |conn| User_::read(&conn, user_id)).await??;
let edit_id = data.edit_id; let edit_id = data.edit_id;
let orig_comment = let orig_comment =
blocking(pool, move |conn| CommentView::read(&conn, edit_id, None)).await??; blocking(pool, move |conn| CommentView::read(&conn, edit_id, None)).await??;
let mut editors: Vec<i32> = vec![orig_comment.creator_id]; // Check for a site ban
let mut moderators: Vec<i32> = vec![]; let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
if user.banned {
return Err(APIError::err("site_ban").into());
}
// Check for a community ban
let community_id = orig_comment.community_id; let community_id = orig_comment.community_id;
moderators.append( let is_banned =
&mut blocking(pool, move |conn| { move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
CommunityModeratorView::for_community(&conn, community_id) if blocking(pool, is_banned).await? {
.map(|v| v.into_iter().map(|m| m.user_id).collect()) return Err(APIError::err("community_ban").into());
}) }
.await??,
);
moderators.append(
&mut blocking(pool, move |conn| {
UserView::admins(conn).map(|v| v.into_iter().map(|a| a.id).collect())
})
.await??,
);
editors.extend(&moderators);
// You are allowed to mark the comment as read even if you're banned.
if data.read.is_none() {
// Verify its the creator or a mod, or an admin
if !editors.contains(&user_id) {
return Err(APIError::err("no_comment_edit_allowed").into());
}
// Check for a community ban
let community_id = orig_comment.community_id;
let is_banned =
move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
if blocking(pool, is_banned).await? {
return Err(APIError::err("community_ban").into());
}
// Check for a site ban // Verify that only the creator can edit
if user.banned { if user_id != orig_comment.creator_id {
return Err(APIError::err("site_ban").into()); return Err(APIError::err("no_comment_edit_allowed").into());
}
} else {
// check that user can mark as read
let parent_id = orig_comment.parent_id;
match parent_id {
Some(pid) => {
let parent_comment =
blocking(pool, move |conn| CommentView::read(&conn, pid, None)).await??;
if user_id != parent_comment.creator_id {
return Err(APIError::err("no_comment_edit_allowed").into());
}
}
None => {
let parent_post_id = orig_comment.post_id;
let parent_post = blocking(pool, move |conn| Post::read(conn, parent_post_id)).await??;
if user_id != parent_post.creator_id {
return Err(APIError::err("no_comment_edit_allowed").into());
}
}
}
} }
// Do the update
let content_slurs_removed = remove_slurs(&data.content.to_owned()); let content_slurs_removed = remove_slurs(&data.content.to_owned());
let edit_id = data.edit_id;
let updated_comment = match blocking(pool, move |conn| {
Comment::update_content(conn, edit_id, &content_slurs_removed)
})
.await?
{
Ok(comment) => comment,
Err(_e) => return Err(APIError::err("couldnt_update_comment").into()),
};
// Send the apub update
updated_comment
.send_update(&user, &self.client, pool)
.await?;
// Do the mentions / recipients
let post_id = orig_comment.post_id;
let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
let updated_comment_content = updated_comment.content.to_owned();
let mentions = scrape_text_for_mentions(&updated_comment_content);
let recipient_ids =
send_local_notifs(mentions, updated_comment, user, post, pool, false).await?;
let edit_id = data.edit_id; let edit_id = data.edit_id;
let read_comment = blocking(pool, move |conn| Comment::read(conn, edit_id)).await??; let comment_view = blocking(pool, move |conn| {
CommentView::read(conn, edit_id, Some(user_id))
let comment_form = { })
if data.read.is_none() { .await??;
// the ban etc checks should been made and have passed
// the comment can be properly edited let mut res = CommentResponse {
let post_removed = if moderators.contains(&user_id) { comment: comment_view,
data.removed recipient_ids,
} else { };
Some(read_comment.removed)
}; if let Some(ws) = websocket_info {
ws.chatserver.do_send(SendComment {
CommentForm { op: UserOperation::EditComment,
content: content_slurs_removed, comment: res.clone(),
parent_id: read_comment.parent_id, my_id: ws.id,
post_id: read_comment.post_id, });
creator_id: read_comment.creator_id,
removed: post_removed.to_owned(), // strip out the recipient_ids, so that
deleted: data.deleted.to_owned(), // users don't get double notifs
read: Some(read_comment.read), res.recipient_ids = Vec::new();
published: None, }
updated: Some(naive_now()),
ap_id: read_comment.ap_id, Ok(res)
local: read_comment.local, }
} }
} else {
// the only field that can be updated it the read field #[async_trait::async_trait(?Send)]
CommentForm { impl Perform for Oper<DeleteComment> {
content: read_comment.content, type Response = CommentResponse;
parent_id: read_comment.parent_id,
post_id: read_comment.post_id, async fn perform(
creator_id: read_comment.creator_id, &self,
removed: Some(read_comment.removed).to_owned(), pool: &DbPool,
deleted: Some(read_comment.deleted).to_owned(), websocket_info: Option<WebsocketInfo>,
read: data.read.to_owned(), ) -> Result<CommentResponse, LemmyError> {
published: None, let data: &DeleteComment = &self.data;
updated: orig_comment.updated,
ap_id: read_comment.ap_id, let claims = match Claims::decode(&data.auth) {
local: read_comment.local, Ok(claims) => claims.claims,
} Err(_e) => return Err(APIError::err("not_logged_in").into()),
}
}; };
let user_id = claims.id;
let edit_id = data.edit_id; let edit_id = data.edit_id;
let comment_form2 = comment_form.clone(); let orig_comment =
blocking(pool, move |conn| CommentView::read(&conn, edit_id, None)).await??;
// Check for a site ban
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
if user.banned {
return Err(APIError::err("site_ban").into());
}
// Check for a community ban
let community_id = orig_comment.community_id;
let is_banned =
move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
if blocking(pool, is_banned).await? {
return Err(APIError::err("community_ban").into());
}
// Verify that only the creator can delete
if user_id != orig_comment.creator_id {
return Err(APIError::err("no_comment_edit_allowed").into());
}
// Do the delete
let deleted = data.deleted;
let updated_comment = match blocking(pool, move |conn| { let updated_comment = match blocking(pool, move |conn| {
Comment::update(conn, edit_id, &comment_form2) Comment::update_deleted(conn, edit_id, deleted)
}) })
.await? .await?
{ {
@ -360,61 +386,142 @@ impl Perform for Oper<EditComment> {
Err(_e) => return Err(APIError::err("couldnt_update_comment").into()), Err(_e) => return Err(APIError::err("couldnt_update_comment").into()),
}; };
if data.read.is_none() { // Send the apub message
if let Some(deleted) = data.deleted.to_owned() { if deleted {
if deleted { updated_comment
updated_comment .send_delete(&user, &self.client, pool)
.send_delete(&user, &self.client, pool) .await?;
.await?; } else {
} else { updated_comment
updated_comment .send_undo_delete(&user, &self.client, pool)
.send_undo_delete(&user, &self.client, pool) .await?;
.await?;
}
} else if let Some(removed) = data.removed.to_owned() {
if moderators.contains(&user_id) {
if removed {
updated_comment
.send_remove(&user, &self.client, pool)
.await?;
} else {
updated_comment
.send_undo_remove(&user, &self.client, pool)
.await?;
}
}
} else {
updated_comment
.send_update(&user, &self.client, pool)
.await?;
}
// Mod tables
if moderators.contains(&user_id) {
if let Some(removed) = data.removed.to_owned() {
let form = ModRemoveCommentForm {
mod_user_id: user_id,
comment_id: data.edit_id,
removed: Some(removed),
reason: data.reason.to_owned(),
};
blocking(pool, move |conn| ModRemoveComment::create(conn, &form)).await??;
}
}
} }
let post_id = data.post_id; // Refetch it
let edit_id = data.edit_id;
let comment_view = blocking(pool, move |conn| {
CommentView::read(conn, edit_id, Some(user_id))
})
.await??;
// Build the recipients
let post_id = comment_view.post_id;
let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??; let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
let mentions = vec![];
let recipient_ids =
send_local_notifs(mentions, updated_comment, user, post, pool, false).await?;
let mentions = scrape_text_for_mentions(&comment_form.content); let mut res = CommentResponse {
let recipient_ids = send_local_notifs(mentions, updated_comment, user, post, pool).await?; comment: comment_view,
recipient_ids,
};
if let Some(ws) = websocket_info {
ws.chatserver.do_send(SendComment {
op: UserOperation::DeleteComment,
comment: res.clone(),
my_id: ws.id,
});
// strip out the recipient_ids, so that
// users don't get double notifs
res.recipient_ids = Vec::new();
}
Ok(res)
}
}
#[async_trait::async_trait(?Send)]
impl Perform for Oper<RemoveComment> {
type Response = CommentResponse;
async fn perform(
&self,
pool: &DbPool,
websocket_info: Option<WebsocketInfo>,
) -> Result<CommentResponse, LemmyError> {
let data: &RemoveComment = &self.data;
let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
let user_id = claims.id;
let edit_id = data.edit_id;
let orig_comment =
blocking(pool, move |conn| CommentView::read(&conn, edit_id, None)).await??;
// Check for a site ban
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
if user.banned {
return Err(APIError::err("site_ban").into());
}
// Check for a community ban
let community_id = orig_comment.community_id;
let is_banned =
move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
if blocking(pool, is_banned).await? {
return Err(APIError::err("community_ban").into());
}
// Verify that only a mod or admin can remove
let mods_and_admins = blocking(pool, move |conn| {
Community::community_mods_and_admins(conn, community_id)
})
.await??;
if !mods_and_admins.contains(&user_id) {
return Err(APIError::err("not_an_admin").into());
}
// Do the remove
let removed = data.removed;
let updated_comment = match blocking(pool, move |conn| {
Comment::update_removed(conn, edit_id, removed)
})
.await?
{
Ok(comment) => comment,
Err(_e) => return Err(APIError::err("couldnt_update_comment").into()),
};
// Mod tables
let form = ModRemoveCommentForm {
mod_user_id: user_id,
comment_id: data.edit_id,
removed: Some(removed),
reason: data.reason.to_owned(),
};
blocking(pool, move |conn| ModRemoveComment::create(conn, &form)).await??;
// Send the apub message
if removed {
updated_comment
.send_remove(&user, &self.client, pool)
.await?;
} else {
updated_comment
.send_undo_remove(&user, &self.client, pool)
.await?;
}
// Refetch it
let edit_id = data.edit_id; let edit_id = data.edit_id;
let comment_view = blocking(pool, move |conn| { let comment_view = blocking(pool, move |conn| {
CommentView::read(conn, edit_id, Some(user_id)) CommentView::read(conn, edit_id, Some(user_id))
}) })
.await??; .await??;
// Build the recipients
let post_id = comment_view.post_id;
let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
let mentions = vec![];
let recipient_ids =
send_local_notifs(mentions, updated_comment, user, post, pool, false).await?;
let mut res = CommentResponse { let mut res = CommentResponse {
comment: comment_view, comment: comment_view,
recipient_ids, recipient_ids,
@ -422,7 +529,7 @@ impl Perform for Oper<EditComment> {
if let Some(ws) = websocket_info { if let Some(ws) = websocket_info {
ws.chatserver.do_send(SendComment { ws.chatserver.do_send(SendComment {
op: UserOperation::EditComment, op: UserOperation::RemoveComment,
comment: res.clone(), comment: res.clone(),
my_id: ws.id, my_id: ws.id,
}); });
@ -436,6 +543,85 @@ impl Perform for Oper<EditComment> {
} }
} }
#[async_trait::async_trait(?Send)]
impl Perform for Oper<MarkCommentAsRead> {
type Response = CommentResponse;
async fn perform(
&self,
pool: &DbPool,
_websocket_info: Option<WebsocketInfo>,
) -> Result<CommentResponse, LemmyError> {
let data: &MarkCommentAsRead = &self.data;
let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
let user_id = claims.id;
let edit_id = data.edit_id;
let orig_comment =
blocking(pool, move |conn| CommentView::read(&conn, edit_id, None)).await??;
// Check for a site ban
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
if user.banned {
return Err(APIError::err("site_ban").into());
}
// Check for a community ban
let community_id = orig_comment.community_id;
let is_banned =
move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
if blocking(pool, is_banned).await? {
return Err(APIError::err("community_ban").into());
}
// Verify that only the recipient can mark as read
// Needs to fetch the parent comment / post to get the recipient
let parent_id = orig_comment.parent_id;
match parent_id {
Some(pid) => {
let parent_comment =
blocking(pool, move |conn| CommentView::read(&conn, pid, None)).await??;
if user_id != parent_comment.creator_id {
return Err(APIError::err("no_comment_edit_allowed").into());
}
}
None => {
let parent_post_id = orig_comment.post_id;
let parent_post = blocking(pool, move |conn| Post::read(conn, parent_post_id)).await??;
if user_id != parent_post.creator_id {
return Err(APIError::err("no_comment_edit_allowed").into());
}
}
}
// Do the mark as read
let read = data.read;
match blocking(pool, move |conn| Comment::update_read(conn, edit_id, read)).await? {
Ok(comment) => comment,
Err(_e) => return Err(APIError::err("couldnt_update_comment").into()),
};
// Refetch it
let edit_id = data.edit_id;
let comment_view = blocking(pool, move |conn| {
CommentView::read(conn, edit_id, Some(user_id))
})
.await??;
let res = CommentResponse {
comment: comment_view,
recipient_ids: Vec::new(),
};
Ok(res)
}
}
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl Perform for Oper<SaveComment> { impl Perform for Oper<SaveComment> {
type Response = CommentResponse; type Response = CommentResponse;
@ -512,8 +698,12 @@ impl Perform for Oper<CreateCommentLike> {
} }
} }
let comment_id = data.comment_id;
let orig_comment =
blocking(pool, move |conn| CommentView::read(&conn, comment_id, None)).await??;
// Check for a community ban // Check for a community ban
let post_id = data.post_id; let post_id = orig_comment.post_id;
let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??; let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
let community_id = post.community_id; let community_id = post.community_id;
let is_banned = let is_banned =
@ -550,7 +740,7 @@ impl Perform for Oper<CreateCommentLike> {
let like_form = CommentLikeForm { let like_form = CommentLikeForm {
comment_id: data.comment_id, comment_id: data.comment_id,
post_id: data.post_id, post_id,
user_id, user_id,
score: data.score, score: data.score,
}; };
@ -675,9 +865,10 @@ pub async fn send_local_notifs(
user: User_, user: User_,
post: Post, post: Post,
pool: &DbPool, pool: &DbPool,
do_send_email: bool,
) -> Result<Vec<i32>, LemmyError> { ) -> Result<Vec<i32>, LemmyError> {
let ids = blocking(pool, move |conn| { let ids = blocking(pool, move |conn| {
do_send_local_notifs(conn, &mentions, &comment, &user, &post) do_send_local_notifs(conn, &mentions, &comment, &user, &post, do_send_email)
}) })
.await?; .await?;
@ -690,6 +881,7 @@ fn do_send_local_notifs(
comment: &Comment, comment: &Comment,
user: &User_, user: &User_,
post: &Post, post: &Post,
do_send_email: bool,
) -> Vec<i32> { ) -> Vec<i32> {
let mut recipient_ids = Vec::new(); let mut recipient_ids = Vec::new();
let hostname = &format!("https://{}", Settings::get().hostname); let hostname = &format!("https://{}", Settings::get().hostname);
@ -720,7 +912,7 @@ fn do_send_local_notifs(
}; };
// Send an email to those users that have notifications on // Send an email to those users that have notifications on
if mention_user.send_notifications_to_email { if do_send_email && mention_user.send_notifications_to_email {
if let Some(mention_email) = mention_user.email { if let Some(mention_email) = mention_user.email {
let subject = &format!("{} - Mentioned by {}", Settings::get().hostname, user.name,); let subject = &format!("{} - Mentioned by {}", Settings::get().hostname, user.name,);
let html = &format!( let html = &format!(
@ -744,7 +936,7 @@ fn do_send_local_notifs(
if let Ok(parent_user) = User_::read(&conn, parent_comment.creator_id) { if let Ok(parent_user) = User_::read(&conn, parent_comment.creator_id) {
recipient_ids.push(parent_user.id); recipient_ids.push(parent_user.id);
if parent_user.send_notifications_to_email { if do_send_email && parent_user.send_notifications_to_email {
if let Some(comment_reply_email) = parent_user.email { if let Some(comment_reply_email) = parent_user.email {
let subject = &format!("{} - Reply from {}", Settings::get().hostname, user.name,); let subject = &format!("{} - Reply from {}", Settings::get().hostname, user.name,);
let html = &format!( let html = &format!(
@ -767,7 +959,7 @@ fn do_send_local_notifs(
if let Ok(parent_user) = User_::read(&conn, post.creator_id) { if let Ok(parent_user) = User_::read(&conn, post.creator_id) {
recipient_ids.push(parent_user.id); recipient_ids.push(parent_user.id);
if parent_user.send_notifications_to_email { if do_send_email && parent_user.send_notifications_to_email {
if let Some(post_reply_email) = parent_user.email { if let Some(post_reply_email) = parent_user.email {
let subject = &format!("{} - Reply from {}", Settings::get().hostname, user.name,); let subject = &format!("{} - Reply from {}", Settings::get().hostname, user.name,);
let html = &format!( let html = &format!(

@ -10,7 +10,6 @@ use crate::{
}, },
DbPool, DbPool,
}; };
use diesel::PgConnection;
use lemmy_db::{naive_now, Bannable, Crud, Followable, Joinable, SortType}; use lemmy_db::{naive_now, Bannable, Crud, Followable, Joinable, SortType};
use lemmy_utils::{ use lemmy_utils::{
generate_actor_keypair, generate_actor_keypair,
@ -1078,16 +1077,3 @@ impl Perform for Oper<TransferCommunity> {
}) })
} }
} }
pub fn community_mods_and_admins(
conn: &PgConnection,
community_id: i32,
) -> Result<Vec<i32>, LemmyError> {
let mut editors: Vec<i32> = Vec::new();
editors.append(
&mut CommunityModeratorView::for_community(conn, community_id)
.map(|v| v.into_iter().map(|m| m.user_id).collect())?,
);
editors.append(&mut UserView::admins(conn).map(|v| v.into_iter().map(|a| a.id).collect())?);
Ok(editors)
}

@ -936,9 +936,11 @@ impl Perform for Oper<MarkAllAsRead> {
.await??; .await??;
// TODO: this should probably be a bulk operation // TODO: this should probably be a bulk operation
// Not easy to do as a bulk operation,
// because recipient_id isn't in the comment table
for reply in &replies { for reply in &replies {
let reply_id = reply.id; let reply_id = reply.id;
let mark_as_read = move |conn: &'_ _| Comment::mark_as_read(conn, reply_id); let mark_as_read = move |conn: &'_ _| Comment::update_read(conn, reply_id, true);
if blocking(pool, mark_as_read).await?.is_err() { if blocking(pool, mark_as_read).await?.is_err() {
return Err(APIError::err("couldnt_update_comment").into()); return Err(APIError::err("couldnt_update_comment").into());
} }

@ -393,7 +393,7 @@ async fn receive_create_comment(
// anyway. // anyway.
let mentions = scrape_text_for_mentions(&inserted_comment.content); let mentions = scrape_text_for_mentions(&inserted_comment.content);
let recipient_ids = let recipient_ids =
send_local_notifs(mentions, inserted_comment.clone(), user, post, pool).await?; send_local_notifs(mentions, inserted_comment.clone(), user, post, pool, true).await?;
// Refetch the view // Refetch the view
let comment_view = blocking(pool, move |conn| { let comment_view = blocking(pool, move |conn| {
@ -558,7 +558,7 @@ async fn receive_update_comment(
let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??; let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
let mentions = scrape_text_for_mentions(&updated_comment.content); let mentions = scrape_text_for_mentions(&updated_comment.content);
let recipient_ids = send_local_notifs(mentions, updated_comment, user, post, pool).await?; let recipient_ids = send_local_notifs(mentions, updated_comment, user, post, pool, false).await?;
// Refetch the view // Refetch the view
let comment_view = let comment_view =

@ -83,6 +83,12 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimit) {
.wrap(rate_limit.message()) .wrap(rate_limit.message())
.route("", web::post().to(route_post::<CreateComment>)) .route("", web::post().to(route_post::<CreateComment>))
.route("", web::put().to(route_post::<EditComment>)) .route("", web::put().to(route_post::<EditComment>))
.route("/delete", web::post().to(route_post::<DeleteComment>))
.route("/remove", web::post().to(route_post::<RemoveComment>))
.route(
"/mark_as_read",
web::post().to(route_post::<MarkCommentAsRead>),
)
.route("/like", web::post().to(route_post::<CreateCommentLike>)) .route("/like", web::post().to(route_post::<CreateCommentLike>))
.route("/save", web::put().to(route_post::<SaveComment>)), .route("/save", web::put().to(route_post::<SaveComment>)),
) )

@ -28,6 +28,9 @@ pub enum UserOperation {
GetCommunity, GetCommunity,
CreateComment, CreateComment,
EditComment, EditComment,
DeleteComment,
RemoveComment,
MarkCommentAsRead,
SaveComment, SaveComment,
CreateCommentLike, CreateCommentLike,
GetPosts, GetPosts,

@ -506,6 +506,9 @@ impl ChatServer {
// Comment ops // Comment ops
UserOperation::CreateComment => do_user_operation::<CreateComment>(args).await, UserOperation::CreateComment => do_user_operation::<CreateComment>(args).await,
UserOperation::EditComment => do_user_operation::<EditComment>(args).await, UserOperation::EditComment => do_user_operation::<EditComment>(args).await,
UserOperation::DeleteComment => do_user_operation::<DeleteComment>(args).await,
UserOperation::RemoveComment => do_user_operation::<RemoveComment>(args).await,
UserOperation::MarkCommentAsRead => do_user_operation::<MarkCommentAsRead>(args).await,
UserOperation::SaveComment => do_user_operation::<SaveComment>(args).await, UserOperation::SaveComment => do_user_operation::<SaveComment>(args).await,
UserOperation::GetComments => do_user_operation::<GetComments>(args).await, UserOperation::GetComments => do_user_operation::<GetComments>(args).await,
UserOperation::CreateCommentLike => do_user_operation::<CreateCommentLike>(args).await, UserOperation::CreateCommentLike => do_user_operation::<CreateCommentLike>(args).await,

@ -11,6 +11,8 @@ import {
GetFollowedCommunitiesResponse, GetFollowedCommunitiesResponse,
GetPostResponse, GetPostResponse,
CommentForm, CommentForm,
DeleteCommentForm,
RemoveCommentForm,
CommentResponse, CommentResponse,
CommunityForm, CommunityForm,
DeleteCommunityForm, DeleteCommunityForm,
@ -383,7 +385,6 @@ describe('main', () => {
let unlikeCommentForm: CommentLikeForm = { let unlikeCommentForm: CommentLikeForm = {
comment_id: createResponse.comment.id, comment_id: createResponse.comment.id,
score: 0, score: 0,
post_id: 2,
auth: lemmyAlphaAuth, auth: lemmyAlphaAuth,
}; };
@ -621,19 +622,16 @@ describe('main', () => {
expect(createCommentRes.comment.content).toBe(commentContent); expect(createCommentRes.comment.content).toBe(commentContent);
// lemmy_beta deletes the comment // lemmy_beta deletes the comment
let deleteCommentForm: CommentForm = { let deleteCommentForm: DeleteCommentForm = {
content: commentContent,
edit_id: createCommentRes.comment.id, edit_id: createCommentRes.comment.id,
post_id: createPostRes.post.id,
deleted: true, deleted: true,
auth: lemmyBetaAuth, auth: lemmyBetaAuth,
creator_id: createCommentRes.comment.creator_id,
}; };
let deleteCommentRes: CommentResponse = await fetch( let deleteCommentRes: CommentResponse = await fetch(
`${lemmyBetaApiUrl}/comment`, `${lemmyBetaApiUrl}/comment/delete`,
{ {
method: 'PUT', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
@ -650,19 +648,16 @@ describe('main', () => {
expect(getPostRes.comments[0].deleted).toBe(true); expect(getPostRes.comments[0].deleted).toBe(true);
// lemmy_beta undeletes the comment // lemmy_beta undeletes the comment
let undeleteCommentForm: CommentForm = { let undeleteCommentForm: DeleteCommentForm = {
content: commentContent,
edit_id: createCommentRes.comment.id, edit_id: createCommentRes.comment.id,
post_id: createPostRes.post.id,
deleted: false, deleted: false,
auth: lemmyBetaAuth, auth: lemmyBetaAuth,
creator_id: createCommentRes.comment.creator_id,
}; };
let undeleteCommentRes: CommentResponse = await fetch( let undeleteCommentRes: CommentResponse = await fetch(
`${lemmyBetaApiUrl}/comment`, `${lemmyBetaApiUrl}/comment/delete`,
{ {
method: 'PUT', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
@ -889,19 +884,16 @@ describe('main', () => {
expect(createCommentRes.comment.content).toBe(commentContent); expect(createCommentRes.comment.content).toBe(commentContent);
// lemmy_beta removes the comment // lemmy_beta removes the comment
let removeCommentForm: CommentForm = { let removeCommentForm: RemoveCommentForm = {
content: commentContent,
edit_id: createCommentRes.comment.id, edit_id: createCommentRes.comment.id,
post_id: createPostRes.post.id,
removed: true, removed: true,
auth: lemmyBetaAuth, auth: lemmyBetaAuth,
creator_id: createCommentRes.comment.creator_id,
}; };
let removeCommentRes: CommentResponse = await fetch( let removeCommentRes: CommentResponse = await fetch(
`${lemmyBetaApiUrl}/comment`, `${lemmyBetaApiUrl}/comment/remove`,
{ {
method: 'PUT', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
@ -918,19 +910,16 @@ describe('main', () => {
expect(getPostRes.comments[0].removed).toBe(true); expect(getPostRes.comments[0].removed).toBe(true);
// lemmy_beta undeletes the comment // lemmy_beta undeletes the comment
let unremoveCommentForm: CommentForm = { let unremoveCommentForm: RemoveCommentForm = {
content: commentContent,
edit_id: createCommentRes.comment.id, edit_id: createCommentRes.comment.id,
post_id: createPostRes.post.id,
removed: false, removed: false,
auth: lemmyBetaAuth, auth: lemmyBetaAuth,
creator_id: createCommentRes.comment.creator_id,
}; };
let unremoveCommentRes: CommentResponse = await fetch( let unremoveCommentRes: CommentResponse = await fetch(
`${lemmyBetaApiUrl}/comment`, `${lemmyBetaApiUrl}/comment/remove`,
{ {
method: 'PUT', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },

@ -3,7 +3,9 @@ import { Link } from 'inferno-router';
import { import {
CommentNode as CommentNodeI, CommentNode as CommentNodeI,
CommentLikeForm, CommentLikeForm,
CommentForm as CommentFormI, DeleteCommentForm,
RemoveCommentForm,
MarkCommentAsReadForm,
MarkUserMentionAsReadForm, MarkUserMentionAsReadForm,
SaveCommentForm, SaveCommentForm,
BanFromCommunityForm, BanFromCommunityForm,
@ -848,16 +850,12 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
} }
handleDeleteClick(i: CommentNode) { handleDeleteClick(i: CommentNode) {
let deleteForm: CommentFormI = { let deleteForm: DeleteCommentForm = {
content: i.props.node.comment.content,
edit_id: i.props.node.comment.id, 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, deleted: !i.props.node.comment.deleted,
auth: null, auth: null,
}; };
WebSocketService.Instance.editComment(deleteForm); WebSocketService.Instance.deleteComment(deleteForm);
} }
handleSaveCommentClick(i: CommentNode) { handleSaveCommentClick(i: CommentNode) {
@ -901,7 +899,6 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
let form: CommentLikeForm = { let form: CommentLikeForm = {
comment_id: i.comment.id, comment_id: i.comment.id,
post_id: i.comment.post_id,
score: this.state.my_vote, score: this.state.my_vote,
}; };
@ -929,7 +926,6 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
let form: CommentLikeForm = { let form: CommentLikeForm = {
comment_id: i.comment.id, comment_id: i.comment.id,
post_id: i.comment.post_id,
score: this.state.my_vote, score: this.state.my_vote,
}; };
@ -950,17 +946,13 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
handleModRemoveSubmit(i: CommentNode) { handleModRemoveSubmit(i: CommentNode) {
event.preventDefault(); event.preventDefault();
let form: CommentFormI = { let form: RemoveCommentForm = {
content: i.props.node.comment.content,
edit_id: i.props.node.comment.id, 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,
removed: !i.props.node.comment.removed, removed: !i.props.node.comment.removed,
reason: i.state.removeReason, reason: i.state.removeReason,
auth: null, auth: null,
}; };
WebSocketService.Instance.editComment(form); WebSocketService.Instance.removeComment(form);
i.state.showRemoveDialog = false; i.state.showRemoveDialog = false;
i.setState(i.state); i.setState(i.state);
@ -975,16 +967,12 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
}; };
WebSocketService.Instance.markUserMentionAsRead(form); WebSocketService.Instance.markUserMentionAsRead(form);
} else { } else {
let form: CommentFormI = { let form: MarkCommentAsReadForm = {
content: i.props.node.comment.content,
edit_id: i.props.node.comment.id, 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,
read: !i.props.node.comment.read, read: !i.props.node.comment.read,
auth: null, auth: null,
}; };
WebSocketService.Instance.editComment(form); WebSocketService.Instance.markCommentAsRead(form);
} }
i.state.readLoading = true; i.state.readLoading = true;

@ -409,7 +409,11 @@ export class Community extends Component<any, State> {
this.state.comments = data.comments; this.state.comments = data.comments;
this.state.loading = false; this.state.loading = false;
this.setState(this.state); this.setState(this.state);
} else if (res.op == UserOperation.EditComment) { } else if (
res.op == UserOperation.EditComment ||
res.op == UserOperation.DeleteComment ||
res.op == UserOperation.RemoveComment
) {
let data = res.data as CommentResponse; let data = res.data as CommentResponse;
editCommentRes(data, this.state.comments); editCommentRes(data, this.state.comments);
this.setState(this.state); this.setState(this.state);

@ -484,9 +484,16 @@ export class Inbox extends Component<any, InboxState> {
this.setState(this.state); this.setState(this.state);
} else if (res.op == UserOperation.MarkAllAsRead) { } else if (res.op == UserOperation.MarkAllAsRead) {
// Moved to be instant // Moved to be instant
} else if (res.op == UserOperation.EditComment) { } else if (
res.op == UserOperation.EditComment ||
res.op == UserOperation.DeleteComment ||
res.op == UserOperation.RemoveComment
) {
let data = res.data as CommentResponse; let data = res.data as CommentResponse;
editCommentRes(data, this.state.replies); editCommentRes(data, this.state.replies);
this.setState(this.state);
} else if (res.op == UserOperation.MarkCommentAsRead) {
let data = res.data as CommentResponse;
// If youre in the unread view, just remove it from the list // If youre in the unread view, just remove it from the list
if (this.state.unreadOrAll == UnreadOrAll.Unread && data.comment.read) { if (this.state.unreadOrAll == UnreadOrAll.Unread && data.comment.read) {

@ -701,7 +701,11 @@ export class Main extends Component<any, MainState> {
this.state.comments = data.comments; this.state.comments = data.comments;
this.state.loading = false; this.state.loading = false;
this.setState(this.state); this.setState(this.state);
} else if (res.op == UserOperation.EditComment) { } else if (
res.op == UserOperation.EditComment ||
res.op == UserOperation.DeleteComment ||
res.op == UserOperation.RemoveComment
) {
let data = res.data as CommentResponse; let data = res.data as CommentResponse;
editCommentRes(data, this.state.comments); editCommentRes(data, this.state.comments);
this.setState(this.state); this.setState(this.state);

@ -8,7 +8,7 @@ import {
GetPostResponse, GetPostResponse,
PostResponse, PostResponse,
Comment, Comment,
CommentForm as CommentFormI, MarkCommentAsReadForm,
CommentResponse, CommentResponse,
CommentSortType, CommentSortType,
CommentViewType, CommentViewType,
@ -167,16 +167,12 @@ export class Post extends Component<any, PostState> {
UserService.Instance.user && UserService.Instance.user &&
UserService.Instance.user.id == parent_user_id UserService.Instance.user.id == parent_user_id
) { ) {
let form: CommentFormI = { let form: MarkCommentAsReadForm = {
content: found.content,
edit_id: found.id, edit_id: found.id,
creator_id: found.creator_id,
post_id: found.post_id,
parent_id: found.parent_id,
read: true, read: true,
auth: null, auth: null,
}; };
WebSocketService.Instance.editComment(form); WebSocketService.Instance.markCommentAsRead(form);
UserService.Instance.user.unreadCount--; UserService.Instance.user.unreadCount--;
UserService.Instance.sub.next({ UserService.Instance.sub.next({
user: UserService.Instance.user, user: UserService.Instance.user,
@ -435,7 +431,11 @@ export class Post extends Component<any, PostState> {
this.state.comments.unshift(data.comment); this.state.comments.unshift(data.comment);
this.setState(this.state); this.setState(this.state);
} }
} else if (res.op == UserOperation.EditComment) { } else if (
res.op == UserOperation.EditComment ||
res.op == UserOperation.DeleteComment ||
res.op == UserOperation.RemoveComment
) {
let data = res.data as CommentResponse; let data = res.data as CommentResponse;
editCommentRes(data, this.state.comments); editCommentRes(data, this.state.comments);
this.setState(this.state); this.setState(this.state);

@ -257,7 +257,11 @@ export class UserDetails extends Component<UserDetailsProps, UserDetailsState> {
this.setState({ this.setState({
comments: this.state.comments, comments: this.state.comments,
}); });
} else if (res.op == UserOperation.EditComment) { } else if (
res.op == UserOperation.EditComment ||
res.op == UserOperation.DeleteComment ||
res.op == UserOperation.RemoveComment
) {
const data = res.data as CommentResponse; const data = res.data as CommentResponse;
editCommentRes(data, this.state.comments); editCommentRes(data, this.state.comments);
this.setState({ this.setState({

@ -9,6 +9,9 @@ export enum UserOperation {
GetCommunity, GetCommunity,
CreateComment, CreateComment,
EditComment, EditComment,
DeleteComment,
RemoveComment,
MarkCommentAsRead,
SaveComment, SaveComment,
CreateCommentLike, CreateCommentLike,
GetPosts, GetPosts,
@ -679,14 +682,29 @@ export interface PostResponse {
export interface CommentForm { export interface CommentForm {
content: string; content: string;
post_id: number; post_id?: number;
parent_id?: number; parent_id?: number;
edit_id?: number; edit_id?: number;
creator_id?: number; creator_id?: number;
removed?: boolean; auth: string;
deleted?: boolean; }
export interface DeleteCommentForm {
edit_id: number;
deleted: boolean;
auth: string;
}
export interface RemoveCommentForm {
edit_id: number;
removed: boolean;
reason?: string; reason?: string;
read?: boolean; auth: string;
}
export interface MarkCommentAsReadForm {
edit_id: number;
read: boolean;
auth: string; auth: string;
} }
@ -703,7 +721,6 @@ export interface CommentResponse {
export interface CommentLikeForm { export interface CommentLikeForm {
comment_id: number; comment_id: number;
post_id: number;
score: number; score: number;
auth?: string; auth?: string;
} }
@ -901,6 +918,9 @@ export type MessageType =
| GetPostsForm | GetPostsForm
| GetCommunityForm | GetCommunityForm
| CommentForm | CommentForm
| DeleteCommentForm
| RemoveCommentForm
| MarkCommentAsReadForm
| CommentLikeForm | CommentLikeForm
| SaveCommentForm | SaveCommentForm
| CreatePostLikeForm | CreatePostLikeForm

@ -9,6 +9,9 @@ import {
PostForm, PostForm,
SavePostForm, SavePostForm,
CommentForm, CommentForm,
DeleteCommentForm,
RemoveCommentForm,
MarkCommentAsReadForm,
SaveCommentForm, SaveCommentForm,
CommentLikeForm, CommentLikeForm,
GetPostForm, GetPostForm,
@ -165,14 +168,29 @@ export class WebSocketService {
this.ws.send(this.wsSendWrapper(UserOperation.GetCommunity, form)); this.ws.send(this.wsSendWrapper(UserOperation.GetCommunity, form));
} }
public createComment(commentForm: CommentForm) { public createComment(form: CommentForm) {
this.setAuth(commentForm); this.setAuth(form);
this.ws.send(this.wsSendWrapper(UserOperation.CreateComment, commentForm)); this.ws.send(this.wsSendWrapper(UserOperation.CreateComment, form));
}
public editComment(form: CommentForm) {
this.setAuth(form);
this.ws.send(this.wsSendWrapper(UserOperation.EditComment, form));
} }
public editComment(commentForm: CommentForm) { public deleteComment(form: DeleteCommentForm) {
this.setAuth(commentForm); this.setAuth(form);
this.ws.send(this.wsSendWrapper(UserOperation.EditComment, commentForm)); this.ws.send(this.wsSendWrapper(UserOperation.DeleteComment, form));
}
public removeComment(form: RemoveCommentForm) {
this.setAuth(form);
this.ws.send(this.wsSendWrapper(UserOperation.RemoveComment, form));
}
public markCommentAsRead(form: MarkCommentAsReadForm) {
this.setAuth(form);
this.ws.send(this.wsSendWrapper(UserOperation.MarkCommentAsRead, form));
} }
public likeComment(form: CommentLikeForm) { public likeComment(form: CommentLikeForm) {

Loading…
Cancel
Save