Browse Source

Adding post delete, remove, lock, and sticky.

comment_form_id
Dessalines 2 years ago
parent
commit
2eac037408
  1. 129
      docs/src/contributing_websocket_http_api.md
  2. 40
      server/lemmy_db/src/post.rs
  3. 5
      server/src/api/comment.rs
  4. 513
      server/src/api/post.rs
  5. 4
      server/src/routes/api.rs
  6. 4
      server/src/websocket/mod.rs
  7. 4
      server/src/websocket/server.rs
  8. 84
      ui/src/api_tests/api.spec.ts
  9. 8
      ui/src/components/community.tsx
  10. 4
      ui/src/components/post-form.tsx
  11. 40
      ui/src/components/post-listing.tsx
  12. 8
      ui/src/components/post.tsx
  13. 40
      ui/src/interfaces.ts
  14. 36
      ui/src/services/WebSocketService.ts

129
docs/src/contributing_websocket_http_api.md

@ -1271,8 +1271,9 @@ Only admins can remove a community.
name: String,
url: Option<String>,
body: Option<String>,
nsfw: bool,
community_id: i32,
auth: String
auth: String,
}
}
```
@ -1378,25 +1379,17 @@ Post listing types are `All, Subscribed, Community`
`POST /post/like`
#### Edit Post
Mods and admins can remove and lock a post, creators can delete it.
##### Request
```rust
{
op: "EditPost",
data: {
edit_id: i32,
creator_id: i32,
community_id: i32,
name: String,
url: Option<String>,
body: Option<String>,
removed: Option<bool>,
deleted: Option<bool>,
locked: Option<bool>,
reason: Option<String>,
auth: String
nsfw: bool,
auth: String,
}
}
```
@ -1414,6 +1407,120 @@ Mods and admins can remove and lock a post, creators can delete it.
`PUT /post`
#### Delete Post
##### Request
```rust
{
op: "DeletePost",
data: {
edit_id: i32,
deleted: bool,
auth: String,
}
}
```
##### Response
```rust
{
op: "DeletePost",
data: {
post: PostView
}
}
```
##### HTTP
`POST /post/delete`
#### Remove Post
Only admins and mods can remove a post.
##### Request
```rust
{
op: "RemovePost",
data: {
edit_id: i32,
removed: bool,
reason: Option<String>,
auth: String,
}
}
```
##### Response
```rust
{
op: "RemovePost",
data: {
post: PostView
}
}
```
##### HTTP
`POST /post/remove`
#### Lock Post
Only admins and mods can lock a post.
##### Request
```rust
{
op: "LockPost",
data: {
edit_id: i32,
locked: bool,
auth: String,
}
}
```
##### Response
```rust
{
op: "LockPost",
data: {
post: PostView
}
}
```
##### HTTP
`POST /post/lock`
#### Sticky Post
Only admins and mods can sticky a post.
##### Request
```rust
{
op: "StickyPost",
data: {
edit_id: i32,
stickied: bool,
auth: String,
}
}
```
##### Response
```rust
{
op: "StickyPost",
data: {
post: PostView
}
}
```
##### HTTP
`POST /post/sticky`
#### Save Post
##### Request
```rust

40
server/lemmy_db/src/post.rs

@ -108,6 +108,46 @@ impl Post {
))
.get_result::<Self>(conn)
}
pub fn update_deleted(
conn: &PgConnection,
post_id: i32,
new_deleted: bool,
) -> Result<Self, Error> {
use crate::schema::post::dsl::*;
diesel::update(post.find(post_id))
.set(deleted.eq(new_deleted))
.get_result::<Self>(conn)
}
pub fn update_removed(
conn: &PgConnection,
post_id: i32,
new_removed: bool,
) -> Result<Self, Error> {
use crate::schema::post::dsl::*;
diesel::update(post.find(post_id))
.set(removed.eq(new_removed))
.get_result::<Self>(conn)
}
pub fn update_locked(conn: &PgConnection, post_id: i32, new_locked: bool) -> Result<Self, Error> {
use crate::schema::post::dsl::*;
diesel::update(post.find(post_id))
.set(locked.eq(new_locked))
.get_result::<Self>(conn)
}
pub fn update_stickied(
conn: &PgConnection,
post_id: i32,
new_stickied: bool,
) -> Result<Self, Error> {
use crate::schema::post::dsl::*;
diesel::update(post.find(post_id))
.set(stickied.eq(new_stickied))
.get_result::<Self>(conn)
}
}
impl Crud<PostForm> for Post {

5
server/src/api/comment.rs

@ -162,6 +162,11 @@ impl Perform for Oper<CreateComment> {
return Err(APIError::err("site_ban").into());
}
// Check if post is locked, no new comments
if post.locked {
return Err(APIError::err("locked").into());
}
// Create the comment
let comment_form2 = comment_form.clone();
let inserted_comment =

513
server/src/api/post.rs

@ -13,6 +13,7 @@ use crate::{
};
use lemmy_db::{
comment_view::*,
community::*,
community_view::*,
moderator::*,
naive_now,
@ -96,20 +97,42 @@ pub struct CreatePostLike {
#[derive(Serialize, Deserialize)]
pub struct EditPost {
pub edit_id: i32,
creator_id: i32,
community_id: i32,
name: String,
url: Option<String>,
body: Option<String>,
removed: Option<bool>,
deleted: Option<bool>,
nsfw: bool,
locked: Option<bool>,
stickied: Option<bool>,
auth: String,
}
#[derive(Serialize, Deserialize)]
pub struct DeletePost {
pub edit_id: i32,
deleted: bool,
auth: String,
}
#[derive(Serialize, Deserialize)]
pub struct RemovePost {
pub edit_id: i32,
removed: bool,
reason: Option<String>,
auth: String,
}
#[derive(Serialize, Deserialize)]
pub struct LockPost {
pub edit_id: i32,
locked: bool,
auth: String,
}
#[derive(Serialize, Deserialize)]
pub struct StickyPost {
pub edit_id: i32,
stickied: bool,
auth: String,
}
#[derive(Serialize, Deserialize)]
pub struct SavePost {
post_id: i32,
@ -549,35 +572,10 @@ impl Perform for Oper<EditPost> {
let user_id = claims.id;
let edit_id = data.edit_id;
let read_post = blocking(pool, move |conn| Post::read(conn, edit_id)).await??;
// Verify its the creator or a mod or admin
let community_id = read_post.community_id;
let mut editors: Vec<i32> = vec![read_post.creator_id];
let mut moderators: Vec<i32> = vec![];
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);
if !editors.contains(&user_id) {
return Err(APIError::err("no_post_edit_allowed").into());
}
let orig_post = blocking(pool, move |conn| Post::read(conn, edit_id)).await??;
// Check for a community ban
let community_id = read_post.community_id;
let community_id = orig_post.community_id;
let is_banned =
move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
if blocking(pool, is_banned).await? {
@ -590,55 +588,34 @@ impl Perform for Oper<EditPost> {
return Err(APIError::err("site_ban").into());
}
// Verify that only the creator can edit
if user_id != orig_post.creator_id {
return Err(APIError::err("no_post_edit_allowed").into());
}
// Fetch Iframely and Pictrs cached image
let (iframely_title, iframely_description, iframely_html, pictrs_thumbnail) =
fetch_iframely_and_pictrs_data(&self.client, data.url.to_owned()).await;
let post_form = {
// only modify some properties if they are a moderator
if moderators.contains(&user_id) {
PostForm {
name: data.name.trim().to_owned(),
url: data.url.to_owned(),
body: data.body.to_owned(),
creator_id: read_post.creator_id.to_owned(),
community_id: read_post.community_id,
removed: data.removed.to_owned(),
deleted: data.deleted.to_owned(),
nsfw: data.nsfw,
locked: data.locked.to_owned(),
stickied: data.stickied.to_owned(),
updated: Some(naive_now()),
embed_title: iframely_title,
embed_description: iframely_description,
embed_html: iframely_html,
thumbnail_url: pictrs_thumbnail,
ap_id: read_post.ap_id,
local: read_post.local,
published: None,
}
} else {
PostForm {
name: read_post.name.trim().to_owned(),
url: data.url.to_owned(),
body: data.body.to_owned(),
creator_id: read_post.creator_id.to_owned(),
community_id: read_post.community_id,
removed: Some(read_post.removed),
deleted: data.deleted.to_owned(),
nsfw: data.nsfw,
locked: Some(read_post.locked),
stickied: Some(read_post.stickied),
updated: Some(naive_now()),
embed_title: iframely_title,
embed_description: iframely_description,
embed_html: iframely_html,
thumbnail_url: pictrs_thumbnail,
ap_id: read_post.ap_id,
local: read_post.local,
published: None,
}
}
let post_form = PostForm {
name: data.name.trim().to_owned(),
url: data.url.to_owned(),
body: data.body.to_owned(),
nsfw: data.nsfw,
creator_id: orig_post.creator_id.to_owned(),
community_id: orig_post.community_id,
removed: Some(orig_post.removed),
deleted: Some(orig_post.deleted),
locked: Some(orig_post.locked),
stickied: Some(orig_post.stickied),
updated: Some(naive_now()),
embed_title: iframely_title,
embed_description: iframely_description,
embed_html: iframely_html,
thumbnail_url: pictrs_thumbnail,
ap_id: orig_post.ap_id,
local: orig_post.local,
published: None,
};
let edit_id = data.edit_id;
@ -656,59 +633,87 @@ impl Perform for Oper<EditPost> {
}
};
if moderators.contains(&user_id) {
// Mod tables
if let Some(removed) = data.removed.to_owned() {
let form = ModRemovePostForm {
mod_user_id: user_id,
post_id: data.edit_id,
removed: Some(removed),
reason: data.reason.to_owned(),
};
blocking(pool, move |conn| ModRemovePost::create(conn, &form)).await??;
}
// Send apub update
updated_post.send_update(&user, &self.client, pool).await?;
if let Some(locked) = data.locked.to_owned() {
let form = ModLockPostForm {
mod_user_id: user_id,
post_id: data.edit_id,
locked: Some(locked),
};
blocking(pool, move |conn| ModLockPost::create(conn, &form)).await??;
}
let edit_id = data.edit_id;
let post_view = blocking(pool, move |conn| {
PostView::read(conn, edit_id, Some(user_id))
})
.await??;
if let Some(stickied) = data.stickied.to_owned() {
let form = ModStickyPostForm {
mod_user_id: user_id,
post_id: data.edit_id,
stickied: Some(stickied),
};
blocking(pool, move |conn| ModStickyPost::create(conn, &form)).await??;
}
let res = PostResponse { post: post_view };
if let Some(ws) = websocket_info {
ws.chatserver.do_send(SendPost {
op: UserOperation::EditPost,
post: res.clone(),
my_id: ws.id,
});
}
if let Some(deleted) = data.deleted.to_owned() {
if deleted {
updated_post.send_delete(&user, &self.client, pool).await?;
} else {
updated_post
.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_post.send_remove(&user, &self.client, pool).await?;
} else {
updated_post
.send_undo_remove(&user, &self.client, pool)
.await?;
}
}
Ok(res)
}
}
#[async_trait::async_trait(?Send)]
impl Perform for Oper<DeletePost> {
type Response = PostResponse;
async fn perform(
&self,
pool: &DbPool,
websocket_info: Option<WebsocketInfo>,
) -> Result<PostResponse, LemmyError> {
let data: &DeletePost = &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_post = blocking(pool, move |conn| Post::read(conn, edit_id)).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_post.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_post.creator_id {
return Err(APIError::err("no_post_edit_allowed").into());
}
// Update the post
let edit_id = data.edit_id;
let deleted = data.deleted;
let updated_post = blocking(pool, move |conn| {
Post::update_deleted(conn, edit_id, deleted)
})
.await??;
// apub updates
if deleted {
updated_post.send_delete(&user, &self.client, pool).await?;
} else {
updated_post.send_update(&user, &self.client, pool).await?;
updated_post
.send_undo_delete(&user, &self.client, pool)
.await?;
}
// Refetch the post
let edit_id = data.edit_id;
let post_view = blocking(pool, move |conn| {
PostView::read(conn, edit_id, Some(user_id))
@ -719,7 +724,265 @@ impl Perform for Oper<EditPost> {
if let Some(ws) = websocket_info {
ws.chatserver.do_send(SendPost {
op: UserOperation::EditPost,
op: UserOperation::DeletePost,
post: res.clone(),
my_id: ws.id,
});
}
Ok(res)
}
}
#[async_trait::async_trait(?Send)]
impl Perform for Oper<RemovePost> {
type Response = PostResponse;
async fn perform(
&self,
pool: &DbPool,
websocket_info: Option<WebsocketInfo>,
) -> Result<PostResponse, LemmyError> {
let data: &RemovePost = &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_post = blocking(pool, move |conn| Post::read(conn, edit_id)).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_post.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 mods 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());
}
// Update the post
let edit_id = data.edit_id;
let removed = data.removed;
let updated_post = blocking(pool, move |conn| {
Post::update_removed(conn, edit_id, removed)
})
.await??;
// Mod tables
let form = ModRemovePostForm {
mod_user_id: user_id,
post_id: data.edit_id,
removed: Some(removed),
reason: data.reason.to_owned(),
};
blocking(pool, move |conn| ModRemovePost::create(conn, &form)).await??;
// apub updates
if removed {
updated_post.send_remove(&user, &self.client, pool).await?;
} else {
updated_post
.send_undo_remove(&user, &self.client, pool)
.await?;
}
// Refetch the post
let edit_id = data.edit_id;
let post_view = blocking(pool, move |conn| {
PostView::read(conn, edit_id, Some(user_id))
})
.await??;
let res = PostResponse { post: post_view };
if let Some(ws) = websocket_info {
ws.chatserver.do_send(SendPost {
op: UserOperation::RemovePost,
post: res.clone(),
my_id: ws.id,
});
}
Ok(res)
}
}
#[async_trait::async_trait(?Send)]
impl Perform for Oper<LockPost> {
type Response = PostResponse;
async fn perform(
&self,
pool: &DbPool,
websocket_info: Option<WebsocketInfo>,
) -> Result<PostResponse, LemmyError> {
let data: &LockPost = &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_post = blocking(pool, move |conn| Post::read(conn, edit_id)).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_post.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 mods can lock
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());
}
// Update the post
let edit_id = data.edit_id;
let locked = data.locked;
let updated_post =
blocking(pool, move |conn| Post::update_locked(conn, edit_id, locked)).await??;
// Mod tables
let form = ModLockPostForm {
mod_user_id: user_id,
post_id: data.edit_id,
locked: Some(locked),
};
blocking(pool, move |conn| ModLockPost::create(conn, &form)).await??;
// apub updates
updated_post.send_update(&user, &self.client, pool).await?;
// Refetch the post
let edit_id = data.edit_id;
let post_view = blocking(pool, move |conn| {
PostView::read(conn, edit_id, Some(user_id))
})
.await??;
let res = PostResponse { post: post_view };
if let Some(ws) = websocket_info {
ws.chatserver.do_send(SendPost {
op: UserOperation::LockPost,
post: res.clone(),
my_id: ws.id,
});
}
Ok(res)
}
}
#[async_trait::async_trait(?Send)]
impl Perform for Oper<StickyPost> {
type Response = PostResponse;
async fn perform(
&self,
pool: &DbPool,
websocket_info: Option<WebsocketInfo>,
) -> Result<PostResponse, LemmyError> {
let data: &StickyPost = &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_post = blocking(pool, move |conn| Post::read(conn, edit_id)).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_post.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 mods can sticky
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());
}
// Update the post
let edit_id = data.edit_id;
let stickied = data.stickied;
let updated_post = blocking(pool, move |conn| {
Post::update_stickied(conn, edit_id, stickied)
})
.await??;
// Mod tables
let form = ModStickyPostForm {
mod_user_id: user_id,
post_id: data.edit_id,
stickied: Some(stickied),
};
blocking(pool, move |conn| ModStickyPost::create(conn, &form)).await??;
// Apub updates
// TODO stickied should pry work like locked for ease of use
updated_post.send_update(&user, &self.client, pool).await?;
// Refetch the post
let edit_id = data.edit_id;
let post_view = blocking(pool, move |conn| {
PostView::read(conn, edit_id, Some(user_id))
})
.await??;
let res = PostResponse { post: post_view };
if let Some(ws) = websocket_info {
ws.chatserver.do_send(SendPost {
op: UserOperation::StickyPost,
post: res.clone(),
my_id: ws.id,
});

4
server/src/routes/api.rs

@ -73,6 +73,10 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimit) {
.wrap(rate_limit.message())
.route("", web::get().to(route_get::<GetPost>))
.route("", web::put().to(route_post::<EditPost>))
.route("/delete", web::post().to(route_post::<DeletePost>))
.route("/remove", web::post().to(route_post::<RemovePost>))
.route("/lock", web::post().to(route_post::<LockPost>))
.route("/sticky", web::post().to(route_post::<StickyPost>))
.route("/list", web::get().to(route_get::<GetPosts>))
.route("/like", web::post().to(route_post::<CreatePostLike>))
.route("/save", web::put().to(route_post::<SavePost>)),

4
server/src/websocket/mod.rs

@ -36,6 +36,10 @@ pub enum UserOperation {
GetPosts,
CreatePostLike,
EditPost,
DeletePost,
RemovePost,
LockPost,
StickyPost,
SavePost,
EditCommunity,
DeleteCommunity,

4
server/src/websocket/server.rs

@ -500,6 +500,10 @@ impl ChatServer {
UserOperation::GetPost => do_user_operation::<GetPost>(args).await,
UserOperation::GetPosts => do_user_operation::<GetPosts>(args).await,
UserOperation::EditPost => do_user_operation::<EditPost>(args).await,
UserOperation::DeletePost => do_user_operation::<DeletePost>(args).await,
UserOperation::RemovePost => do_user_operation::<RemovePost>(args).await,
UserOperation::LockPost => do_user_operation::<LockPost>(args).await,
UserOperation::StickyPost => do_user_operation::<StickyPost>(args).await,
UserOperation::CreatePostLike => do_user_operation::<CreatePostLike>(args).await,
UserOperation::SavePost => do_user_operation::<SavePost>(args).await,

84
ui/src/api_tests/api.spec.ts

@ -4,6 +4,9 @@ import {
LoginForm,
LoginResponse,
PostForm,
DeletePostForm,
RemovePostForm,
// TODO need to test LockPost and StickyPost federated
PostResponse,
SearchResponse,
FollowCommunityForm,
@ -100,7 +103,6 @@ describe('main', () => {
name,
auth: lemmyAlphaAuth,
community_id: 2,
creator_id: 2,
nsfw: false,
};
@ -269,7 +271,6 @@ describe('main', () => {
name,
auth: lemmyAlphaAuth,
community_id: 3,
creator_id: 2,
nsfw: false,
};
@ -326,7 +327,6 @@ describe('main', () => {
edit_id: 2,
auth: lemmyAlphaAuth,
community_id: 3,
creator_id: 2,
nsfw: false,
};
@ -587,7 +587,6 @@ describe('main', () => {
name: postName,
auth: lemmyBetaAuth,
community_id: createCommunityRes.community.id,
creator_id: 2,
nsfw: false,
};
@ -673,23 +672,22 @@ describe('main', () => {
expect(getPostUndeleteRes.comments[0].deleted).toBe(false);
// lemmy_beta deletes the post
let deletePostForm: PostForm = {
name: postName,
let deletePostForm: DeletePostForm = {
edit_id: createPostRes.post.id,
auth: lemmyBetaAuth,
community_id: createPostRes.post.community_id,
creator_id: createPostRes.post.creator_id,
nsfw: false,
deleted: true,
auth: lemmyBetaAuth,
};
let deletePostRes: PostResponse = await fetch(`${lemmyBetaApiUrl}/post`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: wrapper(deletePostForm),
}).then(d => d.json());
let deletePostRes: PostResponse = await fetch(
`${lemmyBetaApiUrl}/post/delete`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: wrapper(deletePostForm),
}
).then(d => d.json());
expect(deletePostRes.post.deleted).toBe(true);
// Make sure lemmy_alpha sees the post is deleted
@ -699,20 +697,16 @@ describe('main', () => {
expect(getPostResAgain.post.deleted).toBe(true);
// lemmy_beta undeletes the post
let undeletePostForm: PostForm = {
name: postName,
let undeletePostForm: DeletePostForm = {
edit_id: createPostRes.post.id,
auth: lemmyBetaAuth,
community_id: createPostRes.post.community_id,
creator_id: createPostRes.post.creator_id,
nsfw: false,
deleted: false,
auth: lemmyBetaAuth,
};
let undeletePostRes: PostResponse = await fetch(
`${lemmyBetaApiUrl}/post`,
`${lemmyBetaApiUrl}/post/delete`,
{
method: 'PUT',
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
@ -849,7 +843,6 @@ describe('main', () => {
name: postName,
auth: lemmyBetaAuth,
community_id: createCommunityRes.community.id,
creator_id: 2,
nsfw: false,
};
@ -935,23 +928,22 @@ describe('main', () => {
expect(getPostUnremoveRes.comments[0].removed).toBe(false);
// lemmy_beta deletes the post
let removePostForm: PostForm = {
name: postName,
let removePostForm: RemovePostForm = {
edit_id: createPostRes.post.id,
auth: lemmyBetaAuth,
community_id: createPostRes.post.community_id,
creator_id: createPostRes.post.creator_id,
nsfw: false,
removed: true,
auth: lemmyBetaAuth,
};
let removePostRes: PostResponse = await fetch(`${lemmyBetaApiUrl}/post`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: wrapper(removePostForm),
}).then(d => d.json());
let removePostRes: PostResponse = await fetch(
`${lemmyBetaApiUrl}/post/remove`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: wrapper(removePostForm),
}
).then(d => d.json());
expect(removePostRes.post.removed).toBe(true);
// Make sure lemmy_alpha sees the post is deleted
@ -961,20 +953,16 @@ describe('main', () => {
expect(getPostResAgain.post.removed).toBe(true);
// lemmy_beta unremoves the post
let unremovePostForm: PostForm = {
name: postName,
let unremovePostForm: RemovePostForm = {
edit_id: createPostRes.post.id,
auth: lemmyBetaAuth,
community_id: createPostRes.post.community_id,
creator_id: createPostRes.post.creator_id,
nsfw: false,
removed: false,
auth: lemmyBetaAuth,
};
let unremovePostRes: PostResponse = await fetch(
`${lemmyBetaApiUrl}/post`,
`${lemmyBetaApiUrl}/post/remove`,
{
method: 'PUT',
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
@ -1226,7 +1214,6 @@ describe('main', () => {
name: postName,
auth: lemmyAlphaAuth,
community_id: 2,
creator_id: 2,
nsfw: false,
};
@ -1337,7 +1324,6 @@ describe('main', () => {
name: betaPostName,
auth: lemmyBetaAuth,
community_id: 2,
creator_id: 2,
nsfw: false,
};

8
ui/src/components/community.tsx

@ -380,7 +380,13 @@ export class Community extends Component<any, State> {
this.state.loading = false;
this.setState(this.state);
setupTippy();
} else if (res.op == UserOperation.EditPost) {
} else if (
res.op == UserOperation.EditPost ||
res.op == UserOperation.DeletePost ||
res.op == UserOperation.RemovePost ||
res.op == UserOperation.LockPost ||
res.op == UserOperation.StickyPost
) {
let data = res.data as PostResponse;
editPostFindRes(data, this.state.posts);
this.setState(this.state);

4
ui/src/components/post-form.tsx

@ -71,9 +71,6 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
nsfw: false,
auth: null,
community_id: null,
creator_id: UserService.Instance.user
? UserService.Instance.user.id
: null,
},
communities: [],
loading: false,
@ -99,7 +96,6 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
name: this.props.post.name,
community_id: this.props.post.community_id,
edit_id: this.props.post.id,
creator_id: this.props.post.creator_id,
url: this.props.post.url,
nsfw: this.props.post.nsfw,
auth: null,

40
ui/src/components/post-listing.tsx

@ -4,7 +4,10 @@ import { WebSocketService, UserService } from '../services';
import {
Post,
CreatePostLikeForm,
PostForm as PostFormI,
DeletePostForm,
RemovePostForm,
LockPostForm,
StickyPostForm,
SavePostForm,
CommunityUser,
UserView,
@ -33,7 +36,6 @@ import {
setupTippy,
hostname,
previewLines,
toast,
} from '../utils';
import { i18n } from '../i18next';
@ -1114,18 +1116,12 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
}
handleDeleteClick(i: PostListing) {
let deleteForm: PostFormI = {
body: i.props.post.body,
community_id: i.props.post.community_id,
name: i.props.post.name,
url: i.props.post.url,
let deleteForm: DeletePostForm = {
edit_id: i.props.post.id,
creator_id: i.props.post.creator_id,
deleted: !i.props.post.deleted,
nsfw: i.props.post.nsfw,
auth: null,
};
WebSocketService.Instance.editPost(deleteForm);
WebSocketService.Instance.deletePost(deleteForm);
}
handleSavePostClick(i: PostListing) {
@ -1163,46 +1159,34 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
handleModRemoveSubmit(i: PostListing) {
event.preventDefault();
let form: PostFormI = {
name: i.props.post.name,
community_id: i.props.post.community_id,
let form: RemovePostForm = {
edit_id: i.props.post.id,
creator_id: i.props.post.creator_id,
removed: !i.props.post.removed,
reason: i.state.removeReason,
nsfw: i.props.post.nsfw,
auth: null,
};
WebSocketService.Instance.editPost(form);
WebSocketService.Instance.removePost(form);
i.state.showRemoveDialog = false;
i.setState(i.state);
}
handleModLock(i: PostListing) {
let form: PostFormI = {
name: i.props.post.name,
community_id: i.props.post.community_id,
let form: LockPostForm = {
edit_id: i.props.post.id,
creator_id: i.props.post.creator_id,
nsfw: i.props.post.nsfw,
locked: !i.props.post.locked,
auth: null,
};
WebSocketService.Instance.editPost(form);
WebSocketService.Instance.lockPost(form);
}
handleModSticky(i: PostListing) {
let form: PostFormI = {
name: i.props.post.name,
community_id: i.props.post.community_id,
let form: StickyPostForm = {
edit_id: i.props.post.id,
creator_id: i.props.post.creator_id,
nsfw: i.props.post.nsfw,
stickied: !i.props.post.stickied,
auth: null,
};
WebSocketService.Instance.editPost(form);
WebSocketService.Instance.stickyPost(form);
}
handleModBanFromCommunityShow(i: PostListing) {

8
ui/src/components/post.tsx

@ -452,7 +452,13 @@ export class Post extends Component<any, PostState> {
let data = res.data as PostResponse;
createPostLikeRes(data, this.state.post);
this.setState(this.state);
} else if (res.op == UserOperation.EditPost) {
} else if (
res.op == UserOperation.EditPost ||
res.op == UserOperation.DeletePost ||
res.op == UserOperation.RemovePost ||
res.op == UserOperation.LockPost ||
res.op == UserOperation.StickyPost
) {
let data = res.data as PostResponse;
this.state.post = data.post;
this.setState(this.state);

40
ui/src/interfaces.ts

@ -17,6 +17,10 @@ export enum UserOperation {
GetPosts,
CreatePostLike,
EditPost,
DeletePost,
RemovePost,
LockPost,
StickyPost,
SavePost,
EditCommunity,
DeleteCommunity,
@ -636,19 +640,37 @@ export interface PostForm {
name: string;
url?: string;
body?: string;
community_id: number;
updated?: number;
community_id?: number;
edit_id?: number;
creator_id: number;
removed?: boolean;
deleted?: boolean;
nsfw: boolean;
locked?: boolean;
stickied?: boolean;
auth: string;
}
export interface DeletePostForm {
edit_id: number;
deleted: boolean;
auth: string;
}
export interface RemovePostForm {
edit_id: number;
removed: boolean;
reason?: string;
auth: string;
}
export interface LockPostForm {
edit_id: number;
locked: boolean;
auth: string;
}
export interface StickyPostForm {
edit_id: number;
stickied: boolean;
auth: string;
}
export interface PostFormParams {
name: string;
url?: string;
@ -914,6 +936,10 @@ export type MessageType =
| ListCommunitiesForm
| GetFollowedCommunitiesForm
| PostForm
| DeletePostForm
| RemovePostForm
| LockPostForm
| StickyPostForm
| GetPostForm
| GetPostsForm
| GetCommunityForm

36
ui/src/services/WebSocketService.ts

@ -7,6 +7,10 @@ import {
DeleteCommunityForm,
RemoveCommunityForm,
PostForm,
DeletePostForm,
RemovePostForm,
LockPostForm,
StickyPostForm,
SavePostForm,
CommentForm,
DeleteCommentForm,
@ -153,9 +157,9 @@ export class WebSocketService {
this.ws.send(this.wsSendWrapper(UserOperation.ListCategories, {}));
}
public createPost(postForm: PostForm) {
this.setAuth(postForm);
this.ws.send(this.wsSendWrapper(UserOperation.CreatePost, postForm));
public createPost(form: PostForm) {
this.setAuth(form);
this.ws.send(this.wsSendWrapper(UserOperation.CreatePost, form));
}
public getPost(form: GetPostForm) {
@ -218,9 +222,29 @@ export class WebSocketService {
this.ws.send(this.wsSendWrapper(UserOperation.CreatePostLike, form));
}
public editPost(postForm: PostForm) {
this.setAuth(postForm);
this.ws.send(this.wsSendWrapper(UserOperation.EditPost, postForm));
public editPost(form: PostForm) {
this.setAuth(form);
this.ws.send(this.wsSendWrapper(UserOperation.EditPost, form));
}
public deletePost(form: DeletePostForm) {
this.setAuth(form);
this.ws.send(this.wsSendWrapper(UserOperation.DeletePost, form));
}
public removePost(form: RemovePostForm) {
this.setAuth(form);
this.ws.send(this.wsSendWrapper(UserOperation.RemovePost, form));
}
public lockPost(form: LockPostForm) {
this.setAuth(form);
this.ws.send(this.wsSendWrapper(UserOperation.LockPost, form));
}
public stickyPost(form: StickyPostForm) {
this.setAuth(form);
this.ws.send(this.wsSendWrapper(UserOperation.StickyPost, form));
}
public savePost(form: SavePostForm) {

Loading…
Cancel
Save