mirror of
https://github.com/LemmyNet/lemmy
synced 2024-11-23 13:03:11 +00:00
Added comment delete, remove, read.
This commit is contained in:
parent
ca7d2feedb
commit
fd96dfdb5e
20 changed files with 626 additions and 279 deletions
96
docs/src/contributing_websocket_http_api.md
vendored
96
docs/src/contributing_websocket_http_api.md
vendored
|
@ -1448,7 +1448,6 @@ Mods and admins can remove and lock a post, creators can delete it.
|
|||
data: {
|
||||
content: String,
|
||||
parent_id: Option<i32>,
|
||||
edit_id: Option<i32>,
|
||||
post_id: i32,
|
||||
auth: String
|
||||
}
|
||||
|
@ -1470,7 +1469,7 @@ Mods and admins can remove and lock a post, creators can delete it.
|
|||
|
||||
#### Edit Comment
|
||||
|
||||
Mods and admins can remove a comment, creators can delete it.
|
||||
Only the creator can edit the comment.
|
||||
|
||||
##### Request
|
||||
```rust
|
||||
|
@ -1478,15 +1477,8 @@ Mods and admins can remove a comment, creators can delete it.
|
|||
op: "EditComment",
|
||||
data: {
|
||||
content: String,
|
||||
parent_id: Option<i32>,
|
||||
edit_id: i32,
|
||||
creator_id: i32,
|
||||
post_id: i32,
|
||||
removed: Option<bool>,
|
||||
deleted: Option<bool>,
|
||||
reason: Option<String>,
|
||||
read: Option<bool>,
|
||||
auth: String
|
||||
auth: String,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -1503,6 +1495,89 @@ Mods and admins can remove a comment, creators can delete it.
|
|||
|
||||
`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
|
||||
##### Request
|
||||
```rust
|
||||
|
@ -1538,7 +1613,6 @@ Mods and admins can remove a comment, creators can delete it.
|
|||
op: "CreateCommentLike",
|
||||
data: {
|
||||
comment_id: i32,
|
||||
post_id: i32,
|
||||
score: i16,
|
||||
auth: String
|
||||
}
|
||||
|
|
|
@ -97,14 +97,6 @@ impl Comment {
|
|||
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> {
|
||||
use crate::schema::comment::dsl::*;
|
||||
|
||||
|
@ -116,6 +108,46 @@ impl Comment {
|
|||
))
|
||||
.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)]
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::{
|
||||
naive_now,
|
||||
schema::{community, community_follower, community_moderator, community_user_ban},
|
||||
Bannable,
|
||||
Crud,
|
||||
|
@ -29,7 +30,6 @@ pub struct Community {
|
|||
pub last_refreshed_at: chrono::NaiveDateTime,
|
||||
}
|
||||
|
||||
// TODO add better delete, remove, lock actions here.
|
||||
#[derive(Insertable, AsChangeset, Clone, Serialize, Deserialize, Debug)]
|
||||
#[table_name = "community"]
|
||||
pub struct CommunityForm {
|
||||
|
@ -129,9 +129,24 @@ impl Community {
|
|||
) -> Result<Self, Error> {
|
||||
use crate::schema::community::dsl::*;
|
||||
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)
|
||||
}
|
||||
|
||||
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)]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{schema::private_message, Crud};
|
||||
use crate::{naive_now, schema::private_message, Crud};
|
||||
use diesel::{dsl::*, result::Error, *};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
@ -88,7 +88,7 @@ impl PrivateMessage {
|
|||
) -> Result<Self, Error> {
|
||||
use crate::schema::private_message::dsl::*;
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
|
@ -13,14 +13,13 @@ use crate::{
|
|||
use lemmy_db::{
|
||||
comment::*,
|
||||
comment_view::*,
|
||||
community::Community,
|
||||
community_view::*,
|
||||
moderator::*,
|
||||
naive_now,
|
||||
post::*,
|
||||
site_view::*,
|
||||
user::*,
|
||||
user_mention::*,
|
||||
user_view::*,
|
||||
Crud,
|
||||
Likeable,
|
||||
ListingType,
|
||||
|
@ -44,7 +43,6 @@ use std::str::FromStr;
|
|||
pub struct CreateComment {
|
||||
content: String,
|
||||
parent_id: Option<i32>,
|
||||
edit_id: Option<i32>, // TODO this isn't used
|
||||
pub post_id: i32,
|
||||
auth: String,
|
||||
}
|
||||
|
@ -52,14 +50,29 @@ pub struct CreateComment {
|
|||
#[derive(Serialize, Deserialize)]
|
||||
pub struct EditComment {
|
||||
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,
|
||||
creator_id: i32,
|
||||
pub post_id: i32,
|
||||
removed: Option<bool>,
|
||||
deleted: Option<bool>,
|
||||
auth: String,
|
||||
}
|
||||
|
||||
#[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>,
|
||||
read: Option<bool>,
|
||||
auth: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct MarkCommentAsRead {
|
||||
edit_id: i32,
|
||||
read: bool,
|
||||
auth: String,
|
||||
}
|
||||
|
||||
|
@ -79,7 +92,6 @@ pub struct CommentResponse {
|
|||
#[derive(Serialize, Deserialize)]
|
||||
pub struct CreateCommentLike {
|
||||
comment_id: i32,
|
||||
pub post_id: i32,
|
||||
score: i16,
|
||||
auth: String,
|
||||
}
|
||||
|
@ -150,6 +162,7 @@ impl Perform for Oper<CreateComment> {
|
|||
return Err(APIError::err("site_ban").into());
|
||||
}
|
||||
|
||||
// Create the comment
|
||||
let comment_form2 = comment_form.clone();
|
||||
let inserted_comment =
|
||||
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()),
|
||||
};
|
||||
|
||||
// Necessary to update the ap_id
|
||||
let inserted_comment_id = inserted_comment.id;
|
||||
let updated_comment: Comment = match blocking(pool, move |conn| {
|
||||
let apub_id =
|
||||
|
@ -175,8 +189,15 @@ impl Perform for Oper<CreateComment> {
|
|||
|
||||
// Scan the comment for user mentions, add those rows
|
||||
let mentions = scrape_text_for_mentions(&comment_form.content);
|
||||
let recipient_ids =
|
||||
send_local_notifs(mentions, updated_comment.clone(), user.clone(), post, pool).await?;
|
||||
let recipient_ids = send_local_notifs(
|
||||
mentions,
|
||||
updated_comment.clone(),
|
||||
user.clone(),
|
||||
post,
|
||||
pool,
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// You like your own comment by default
|
||||
let like_form = CommentLikeForm {
|
||||
|
@ -237,37 +258,14 @@ impl Perform for Oper<EditComment> {
|
|||
|
||||
let user_id = claims.id;
|
||||
|
||||
let user = blocking(pool, move |conn| User_::read(&conn, user_id)).await??;
|
||||
|
||||
let edit_id = data.edit_id;
|
||||
let orig_comment =
|
||||
blocking(pool, move |conn| CommentView::read(&conn, edit_id, None)).await??;
|
||||
|
||||
let mut editors: Vec<i32> = vec![orig_comment.creator_id];
|
||||
let mut moderators: Vec<i32> = vec![];
|
||||
|
||||
let community_id = orig_comment.community_id;
|
||||
moderators.append(
|
||||
&mut blocking(pool, move |conn| {
|
||||
CommunityModeratorView::for_community(&conn, community_id)
|
||||
.map(|v| v.into_iter().map(|m| m.user_id).collect())
|
||||
})
|
||||
.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 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
|
||||
|
@ -278,81 +276,16 @@ impl Perform for Oper<EditComment> {
|
|||
return Err(APIError::err("community_ban").into());
|
||||
}
|
||||
|
||||
// Check for a site ban
|
||||
if user.banned {
|
||||
return Err(APIError::err("site_ban").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 {
|
||||
// Verify that only the creator can edit
|
||||
if user_id != orig_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 edit_id = data.edit_id;
|
||||
let read_comment = blocking(pool, move |conn| Comment::read(conn, edit_id)).await??;
|
||||
|
||||
let comment_form = {
|
||||
if data.read.is_none() {
|
||||
// the ban etc checks should been made and have passed
|
||||
// the comment can be properly edited
|
||||
let post_removed = if moderators.contains(&user_id) {
|
||||
data.removed
|
||||
} else {
|
||||
Some(read_comment.removed)
|
||||
};
|
||||
|
||||
CommentForm {
|
||||
content: content_slurs_removed,
|
||||
parent_id: read_comment.parent_id,
|
||||
post_id: read_comment.post_id,
|
||||
creator_id: read_comment.creator_id,
|
||||
removed: post_removed.to_owned(),
|
||||
deleted: data.deleted.to_owned(),
|
||||
read: Some(read_comment.read),
|
||||
published: None,
|
||||
updated: Some(naive_now()),
|
||||
ap_id: read_comment.ap_id,
|
||||
local: read_comment.local,
|
||||
}
|
||||
} else {
|
||||
// the only field that can be updated it the read field
|
||||
CommentForm {
|
||||
content: read_comment.content,
|
||||
parent_id: read_comment.parent_id,
|
||||
post_id: read_comment.post_id,
|
||||
creator_id: read_comment.creator_id,
|
||||
removed: Some(read_comment.removed).to_owned(),
|
||||
deleted: Some(read_comment.deleted).to_owned(),
|
||||
read: data.read.to_owned(),
|
||||
published: None,
|
||||
updated: orig_comment.updated,
|
||||
ap_id: read_comment.ap_id,
|
||||
local: read_comment.local,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let edit_id = data.edit_id;
|
||||
let comment_form2 = comment_form.clone();
|
||||
let updated_comment = match blocking(pool, move |conn| {
|
||||
Comment::update(conn, edit_id, &comment_form2)
|
||||
Comment::update_content(conn, edit_id, &content_slurs_removed)
|
||||
})
|
||||
.await?
|
||||
{
|
||||
|
@ -360,54 +293,19 @@ impl Perform for Oper<EditComment> {
|
|||
Err(_e) => return Err(APIError::err("couldnt_update_comment").into()),
|
||||
};
|
||||
|
||||
if data.read.is_none() {
|
||||
if let Some(deleted) = data.deleted.to_owned() {
|
||||
if deleted {
|
||||
updated_comment
|
||||
.send_delete(&user, &self.client, pool)
|
||||
.await?;
|
||||
} else {
|
||||
updated_comment
|
||||
.send_undo_delete(&user, &self.client, pool)
|
||||
.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 {
|
||||
// Send the apub update
|
||||
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;
|
||||
// Do the mentions / recipients
|
||||
let post_id = orig_comment.post_id;
|
||||
let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
|
||||
|
||||
let mentions = scrape_text_for_mentions(&comment_form.content);
|
||||
let recipient_ids = send_local_notifs(mentions, updated_comment, user, post, pool).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 comment_view = blocking(pool, move |conn| {
|
||||
|
@ -436,6 +334,294 @@ impl Perform for Oper<EditComment> {
|
|||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl Perform for Oper<DeleteComment> {
|
||||
type Response = CommentResponse;
|
||||
|
||||
async fn perform(
|
||||
&self,
|
||||
pool: &DbPool,
|
||||
websocket_info: Option<WebsocketInfo>,
|
||||
) -> Result<CommentResponse, LemmyError> {
|
||||
let data: &DeleteComment = &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 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| {
|
||||
Comment::update_deleted(conn, edit_id, deleted)
|
||||
})
|
||||
.await?
|
||||
{
|
||||
Ok(comment) => comment,
|
||||
Err(_e) => return Err(APIError::err("couldnt_update_comment").into()),
|
||||
};
|
||||
|
||||
// Send the apub message
|
||||
if deleted {
|
||||
updated_comment
|
||||
.send_delete(&user, &self.client, pool)
|
||||
.await?;
|
||||
} else {
|
||||
updated_comment
|
||||
.send_undo_delete(&user, &self.client, pool)
|
||||
.await?;
|
||||
}
|
||||
|
||||
// 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 mentions = vec![];
|
||||
let recipient_ids =
|
||||
send_local_notifs(mentions, updated_comment, user, post, pool, false).await?;
|
||||
|
||||
let mut res = CommentResponse {
|
||||
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 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 mentions = vec![];
|
||||
let recipient_ids =
|
||||
send_local_notifs(mentions, updated_comment, user, post, pool, false).await?;
|
||||
|
||||
let mut res = CommentResponse {
|
||||
comment: comment_view,
|
||||
recipient_ids,
|
||||
};
|
||||
|
||||
if let Some(ws) = websocket_info {
|
||||
ws.chatserver.do_send(SendComment {
|
||||
op: UserOperation::RemoveComment,
|
||||
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<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)]
|
||||
impl Perform for Oper<SaveComment> {
|
||||
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
|
||||
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 community_id = post.community_id;
|
||||
let is_banned =
|
||||
|
@ -550,7 +740,7 @@ impl Perform for Oper<CreateCommentLike> {
|
|||
|
||||
let like_form = CommentLikeForm {
|
||||
comment_id: data.comment_id,
|
||||
post_id: data.post_id,
|
||||
post_id,
|
||||
user_id,
|
||||
score: data.score,
|
||||
};
|
||||
|
@ -675,9 +865,10 @@ pub async fn send_local_notifs(
|
|||
user: User_,
|
||||
post: Post,
|
||||
pool: &DbPool,
|
||||
do_send_email: bool,
|
||||
) -> Result<Vec<i32>, LemmyError> {
|
||||
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?;
|
||||
|
||||
|
@ -690,6 +881,7 @@ fn do_send_local_notifs(
|
|||
comment: &Comment,
|
||||
user: &User_,
|
||||
post: &Post,
|
||||
do_send_email: bool,
|
||||
) -> Vec<i32> {
|
||||
let mut recipient_ids = Vec::new();
|
||||
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
|
||||
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 {
|
||||
let subject = &format!("{} - Mentioned by {}", Settings::get().hostname, user.name,);
|
||||
let html = &format!(
|
||||
|
@ -744,7 +936,7 @@ fn do_send_local_notifs(
|
|||
if let Ok(parent_user) = User_::read(&conn, parent_comment.creator_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 {
|
||||
let subject = &format!("{} - Reply from {}", Settings::get().hostname, user.name,);
|
||||
let html = &format!(
|
||||
|
@ -767,7 +959,7 @@ fn do_send_local_notifs(
|
|||
if let Ok(parent_user) = User_::read(&conn, post.creator_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 {
|
||||
let subject = &format!("{} - Reply from {}", Settings::get().hostname, user.name,);
|
||||
let html = &format!(
|
||||
|
|
|
@ -10,7 +10,6 @@ use crate::{
|
|||
},
|
||||
DbPool,
|
||||
};
|
||||
use diesel::PgConnection;
|
||||
use lemmy_db::{naive_now, Bannable, Crud, Followable, Joinable, SortType};
|
||||
use lemmy_utils::{
|
||||
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??;
|
||||
|
||||
// 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 {
|
||||
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() {
|
||||
return Err(APIError::err("couldnt_update_comment").into());
|
||||
}
|
||||
|
|
|
@ -393,7 +393,7 @@ async fn receive_create_comment(
|
|||
// anyway.
|
||||
let mentions = scrape_text_for_mentions(&inserted_comment.content);
|
||||
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
|
||||
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 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
|
||||
let comment_view =
|
||||
|
|
|
@ -83,6 +83,12 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimit) {
|
|||
.wrap(rate_limit.message())
|
||||
.route("", web::post().to(route_post::<CreateComment>))
|
||||
.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("/save", web::put().to(route_post::<SaveComment>)),
|
||||
)
|
||||
|
|
|
@ -28,6 +28,9 @@ pub enum UserOperation {
|
|||
GetCommunity,
|
||||
CreateComment,
|
||||
EditComment,
|
||||
DeleteComment,
|
||||
RemoveComment,
|
||||
MarkCommentAsRead,
|
||||
SaveComment,
|
||||
CreateCommentLike,
|
||||
GetPosts,
|
||||
|
|
|
@ -506,6 +506,9 @@ impl ChatServer {
|
|||
// Comment ops
|
||||
UserOperation::CreateComment => do_user_operation::<CreateComment>(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::GetComments => do_user_operation::<GetComments>(args).await,
|
||||
UserOperation::CreateCommentLike => do_user_operation::<CreateCommentLike>(args).await,
|
||||
|
|
39
ui/src/api_tests/api.spec.ts
vendored
39
ui/src/api_tests/api.spec.ts
vendored
|
@ -11,6 +11,8 @@ import {
|
|||
GetFollowedCommunitiesResponse,
|
||||
GetPostResponse,
|
||||
CommentForm,
|
||||
DeleteCommentForm,
|
||||
RemoveCommentForm,
|
||||
CommentResponse,
|
||||
CommunityForm,
|
||||
DeleteCommunityForm,
|
||||
|
@ -383,7 +385,6 @@ describe('main', () => {
|
|||
let unlikeCommentForm: CommentLikeForm = {
|
||||
comment_id: createResponse.comment.id,
|
||||
score: 0,
|
||||
post_id: 2,
|
||||
auth: lemmyAlphaAuth,
|
||||
};
|
||||
|
||||
|
@ -621,19 +622,16 @@ describe('main', () => {
|
|||
expect(createCommentRes.comment.content).toBe(commentContent);
|
||||
|
||||
// lemmy_beta deletes the comment
|
||||
let deleteCommentForm: CommentForm = {
|
||||
content: commentContent,
|
||||
let deleteCommentForm: DeleteCommentForm = {
|
||||
edit_id: createCommentRes.comment.id,
|
||||
post_id: createPostRes.post.id,
|
||||
deleted: true,
|
||||
auth: lemmyBetaAuth,
|
||||
creator_id: createCommentRes.comment.creator_id,
|
||||
};
|
||||
|
||||
let deleteCommentRes: CommentResponse = await fetch(
|
||||
`${lemmyBetaApiUrl}/comment`,
|
||||
`${lemmyBetaApiUrl}/comment/delete`,
|
||||
{
|
||||
method: 'PUT',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
|
@ -650,19 +648,16 @@ describe('main', () => {
|
|||
expect(getPostRes.comments[0].deleted).toBe(true);
|
||||
|
||||
// lemmy_beta undeletes the comment
|
||||
let undeleteCommentForm: CommentForm = {
|
||||
content: commentContent,
|
||||
let undeleteCommentForm: DeleteCommentForm = {
|
||||
edit_id: createCommentRes.comment.id,
|
||||
post_id: createPostRes.post.id,
|
||||
deleted: false,
|
||||
auth: lemmyBetaAuth,
|
||||
creator_id: createCommentRes.comment.creator_id,
|
||||
};
|
||||
|
||||
let undeleteCommentRes: CommentResponse = await fetch(
|
||||
`${lemmyBetaApiUrl}/comment`,
|
||||
`${lemmyBetaApiUrl}/comment/delete`,
|
||||
{
|
||||
method: 'PUT',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
|
@ -889,19 +884,16 @@ describe('main', () => {
|
|||
expect(createCommentRes.comment.content).toBe(commentContent);
|
||||
|
||||
// lemmy_beta removes the comment
|
||||
let removeCommentForm: CommentForm = {
|
||||
content: commentContent,
|
||||
let removeCommentForm: RemoveCommentForm = {
|
||||
edit_id: createCommentRes.comment.id,
|
||||
post_id: createPostRes.post.id,
|
||||
removed: true,
|
||||
auth: lemmyBetaAuth,
|
||||
creator_id: createCommentRes.comment.creator_id,
|
||||
};
|
||||
|
||||
let removeCommentRes: CommentResponse = await fetch(
|
||||
`${lemmyBetaApiUrl}/comment`,
|
||||
`${lemmyBetaApiUrl}/comment/remove`,
|
||||
{
|
||||
method: 'PUT',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
|
@ -918,19 +910,16 @@ describe('main', () => {
|
|||
expect(getPostRes.comments[0].removed).toBe(true);
|
||||
|
||||
// lemmy_beta undeletes the comment
|
||||
let unremoveCommentForm: CommentForm = {
|
||||
content: commentContent,
|
||||
let unremoveCommentForm: RemoveCommentForm = {
|
||||
edit_id: createCommentRes.comment.id,
|
||||
post_id: createPostRes.post.id,
|
||||
removed: false,
|
||||
auth: lemmyBetaAuth,
|
||||
creator_id: createCommentRes.comment.creator_id,
|
||||
};
|
||||
|
||||
let unremoveCommentRes: CommentResponse = await fetch(
|
||||
`${lemmyBetaApiUrl}/comment`,
|
||||
`${lemmyBetaApiUrl}/comment/remove`,
|
||||
{
|
||||
method: 'PUT',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
|
|
30
ui/src/components/comment-node.tsx
vendored
30
ui/src/components/comment-node.tsx
vendored
|
@ -3,7 +3,9 @@ import { Link } from 'inferno-router';
|
|||
import {
|
||||
CommentNode as CommentNodeI,
|
||||
CommentLikeForm,
|
||||
CommentForm as CommentFormI,
|
||||
DeleteCommentForm,
|
||||
RemoveCommentForm,
|
||||
MarkCommentAsReadForm,
|
||||
MarkUserMentionAsReadForm,
|
||||
SaveCommentForm,
|
||||
BanFromCommunityForm,
|
||||
|
@ -848,16 +850,12 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
}
|
||||
|
||||
handleDeleteClick(i: CommentNode) {
|
||||
let deleteForm: CommentFormI = {
|
||||
content: i.props.node.comment.content,
|
||||
let deleteForm: DeleteCommentForm = {
|
||||
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);
|
||||
WebSocketService.Instance.deleteComment(deleteForm);
|
||||
}
|
||||
|
||||
handleSaveCommentClick(i: CommentNode) {
|
||||
|
@ -901,7 +899,6 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
|
||||
let form: CommentLikeForm = {
|
||||
comment_id: i.comment.id,
|
||||
post_id: i.comment.post_id,
|
||||
score: this.state.my_vote,
|
||||
};
|
||||
|
||||
|
@ -929,7 +926,6 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
|
||||
let form: CommentLikeForm = {
|
||||
comment_id: i.comment.id,
|
||||
post_id: i.comment.post_id,
|
||||
score: this.state.my_vote,
|
||||
};
|
||||
|
||||
|
@ -950,17 +946,13 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
|
||||
handleModRemoveSubmit(i: CommentNode) {
|
||||
event.preventDefault();
|
||||
let form: CommentFormI = {
|
||||
content: i.props.node.comment.content,
|
||||
let form: RemoveCommentForm = {
|
||||
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,
|
||||
reason: i.state.removeReason,
|
||||
auth: null,
|
||||
};
|
||||
WebSocketService.Instance.editComment(form);
|
||||
WebSocketService.Instance.removeComment(form);
|
||||
|
||||
i.state.showRemoveDialog = false;
|
||||
i.setState(i.state);
|
||||
|
@ -975,16 +967,12 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
};
|
||||
WebSocketService.Instance.markUserMentionAsRead(form);
|
||||
} else {
|
||||
let form: CommentFormI = {
|
||||
content: i.props.node.comment.content,
|
||||
let form: MarkCommentAsReadForm = {
|
||||
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,
|
||||
auth: null,
|
||||
};
|
||||
WebSocketService.Instance.editComment(form);
|
||||
WebSocketService.Instance.markCommentAsRead(form);
|
||||
}
|
||||
|
||||
i.state.readLoading = true;
|
||||
|
|
6
ui/src/components/community.tsx
vendored
6
ui/src/components/community.tsx
vendored
|
@ -409,7 +409,11 @@ export class Community extends Component<any, State> {
|
|||
this.state.comments = data.comments;
|
||||
this.state.loading = false;
|
||||
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;
|
||||
editCommentRes(data, this.state.comments);
|
||||
this.setState(this.state);
|
||||
|
|
9
ui/src/components/inbox.tsx
vendored
9
ui/src/components/inbox.tsx
vendored
|
@ -484,9 +484,16 @@ export class Inbox extends Component<any, InboxState> {
|
|||
this.setState(this.state);
|
||||
} else if (res.op == UserOperation.MarkAllAsRead) {
|
||||
// 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;
|
||||
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 (this.state.unreadOrAll == UnreadOrAll.Unread && data.comment.read) {
|
||||
|
|
6
ui/src/components/main.tsx
vendored
6
ui/src/components/main.tsx
vendored
|
@ -701,7 +701,11 @@ export class Main extends Component<any, MainState> {
|
|||
this.state.comments = data.comments;
|
||||
this.state.loading = false;
|
||||
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;
|
||||
editCommentRes(data, this.state.comments);
|
||||
this.setState(this.state);
|
||||
|
|
16
ui/src/components/post.tsx
vendored
16
ui/src/components/post.tsx
vendored
|
@ -8,7 +8,7 @@ import {
|
|||
GetPostResponse,
|
||||
PostResponse,
|
||||
Comment,
|
||||
CommentForm as CommentFormI,
|
||||
MarkCommentAsReadForm,
|
||||
CommentResponse,
|
||||
CommentSortType,
|
||||
CommentViewType,
|
||||
|
@ -167,16 +167,12 @@ export class Post extends Component<any, PostState> {
|
|||
UserService.Instance.user &&
|
||||
UserService.Instance.user.id == parent_user_id
|
||||
) {
|
||||
let form: CommentFormI = {
|
||||
content: found.content,
|
||||
let form: MarkCommentAsReadForm = {
|
||||
edit_id: found.id,
|
||||
creator_id: found.creator_id,
|
||||
post_id: found.post_id,
|
||||
parent_id: found.parent_id,
|
||||
read: true,
|
||||
auth: null,
|
||||
};
|
||||
WebSocketService.Instance.editComment(form);
|
||||
WebSocketService.Instance.markCommentAsRead(form);
|
||||
UserService.Instance.user.unreadCount--;
|
||||
UserService.Instance.sub.next({
|
||||
user: UserService.Instance.user,
|
||||
|
@ -435,7 +431,11 @@ export class Post extends Component<any, PostState> {
|
|||
this.state.comments.unshift(data.comment);
|
||||
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;
|
||||
editCommentRes(data, this.state.comments);
|
||||
this.setState(this.state);
|
||||
|
|
6
ui/src/components/user-details.tsx
vendored
6
ui/src/components/user-details.tsx
vendored
|
@ -257,7 +257,11 @@ export class UserDetails extends Component<UserDetailsProps, UserDetailsState> {
|
|||
this.setState({
|
||||
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;
|
||||
editCommentRes(data, this.state.comments);
|
||||
this.setState({
|
||||
|
|
30
ui/src/interfaces.ts
vendored
30
ui/src/interfaces.ts
vendored
|
@ -9,6 +9,9 @@ export enum UserOperation {
|
|||
GetCommunity,
|
||||
CreateComment,
|
||||
EditComment,
|
||||
DeleteComment,
|
||||
RemoveComment,
|
||||
MarkCommentAsRead,
|
||||
SaveComment,
|
||||
CreateCommentLike,
|
||||
GetPosts,
|
||||
|
@ -679,14 +682,29 @@ export interface PostResponse {
|
|||
|
||||
export interface CommentForm {
|
||||
content: string;
|
||||
post_id: number;
|
||||
post_id?: number;
|
||||
parent_id?: number;
|
||||
edit_id?: number;
|
||||
creator_id?: number;
|
||||
removed?: boolean;
|
||||
deleted?: boolean;
|
||||
auth: string;
|
||||
}
|
||||
|
||||
export interface DeleteCommentForm {
|
||||
edit_id: number;
|
||||
deleted: boolean;
|
||||
auth: string;
|
||||
}
|
||||
|
||||
export interface RemoveCommentForm {
|
||||
edit_id: number;
|
||||
removed: boolean;
|
||||
reason?: string;
|
||||
read?: boolean;
|
||||
auth: string;
|
||||
}
|
||||
|
||||
export interface MarkCommentAsReadForm {
|
||||
edit_id: number;
|
||||
read: boolean;
|
||||
auth: string;
|
||||
}
|
||||
|
||||
|
@ -703,7 +721,6 @@ export interface CommentResponse {
|
|||
|
||||
export interface CommentLikeForm {
|
||||
comment_id: number;
|
||||
post_id: number;
|
||||
score: number;
|
||||
auth?: string;
|
||||
}
|
||||
|
@ -901,6 +918,9 @@ export type MessageType =
|
|||
| GetPostsForm
|
||||
| GetCommunityForm
|
||||
| CommentForm
|
||||
| DeleteCommentForm
|
||||
| RemoveCommentForm
|
||||
| MarkCommentAsReadForm
|
||||
| CommentLikeForm
|
||||
| SaveCommentForm
|
||||
| CreatePostLikeForm
|
||||
|
|
30
ui/src/services/WebSocketService.ts
vendored
30
ui/src/services/WebSocketService.ts
vendored
|
@ -9,6 +9,9 @@ import {
|
|||
PostForm,
|
||||
SavePostForm,
|
||||
CommentForm,
|
||||
DeleteCommentForm,
|
||||
RemoveCommentForm,
|
||||
MarkCommentAsReadForm,
|
||||
SaveCommentForm,
|
||||
CommentLikeForm,
|
||||
GetPostForm,
|
||||
|
@ -165,14 +168,29 @@ export class WebSocketService {
|
|||
this.ws.send(this.wsSendWrapper(UserOperation.GetCommunity, form));
|
||||
}
|
||||
|
||||
public createComment(commentForm: CommentForm) {
|
||||
this.setAuth(commentForm);
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.CreateComment, commentForm));
|
||||
public createComment(form: CommentForm) {
|
||||
this.setAuth(form);
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.CreateComment, form));
|
||||
}
|
||||
|
||||
public editComment(commentForm: CommentForm) {
|
||||
this.setAuth(commentForm);
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.EditComment, commentForm));
|
||||
public editComment(form: CommentForm) {
|
||||
this.setAuth(form);
|
||||
this.ws.send(this.wsSendWrapper(UserOperation.EditComment, form));
|
||||
}
|
||||
|
||||
public deleteComment(form: DeleteCommentForm) {
|
||||
this.setAuth(form);
|
||||
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) {
|
||||
|
|
Loading…
Reference in a new issue