mirror of
https://github.com/LemmyNet/lemmy
synced 2024-11-27 06:50:20 +00:00
Private message delete and read extracted.
This commit is contained in:
parent
dca38d10eb
commit
0a28ffb9c4
12 changed files with 457 additions and 132 deletions
131
docs/src/contributing_websocket_http_api.md
vendored
131
docs/src/contributing_websocket_http_api.md
vendored
|
@ -489,6 +489,137 @@ Only the first user will be able to be the admin.
|
||||||
|
|
||||||
`PUT /user/mention`
|
`PUT /user/mention`
|
||||||
|
|
||||||
|
#### Get Private Messages
|
||||||
|
##### Request
|
||||||
|
```rust
|
||||||
|
{
|
||||||
|
op: "GetPrivateMessages",
|
||||||
|
data: {
|
||||||
|
unread_only: bool,
|
||||||
|
page: Option<i64>,
|
||||||
|
limit: Option<i64>,
|
||||||
|
auth: String,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
##### Response
|
||||||
|
```rust
|
||||||
|
{
|
||||||
|
op: "GetPrivateMessages",
|
||||||
|
data: {
|
||||||
|
messages: Vec<PrivateMessageView>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### HTTP
|
||||||
|
|
||||||
|
`GET /private_message/list`
|
||||||
|
|
||||||
|
#### Create Private Message
|
||||||
|
##### Request
|
||||||
|
```rust
|
||||||
|
{
|
||||||
|
op: "CreatePrivateMessage",
|
||||||
|
data: {
|
||||||
|
content: String,
|
||||||
|
recipient_id: i32,
|
||||||
|
auth: String,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
##### Response
|
||||||
|
```rust
|
||||||
|
{
|
||||||
|
op: "CreatePrivateMessage",
|
||||||
|
data: {
|
||||||
|
message: PrivateMessageView,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### HTTP
|
||||||
|
|
||||||
|
`POST /private_message`
|
||||||
|
|
||||||
|
#### Edit Private Message
|
||||||
|
##### Request
|
||||||
|
```rust
|
||||||
|
{
|
||||||
|
op: "EditPrivateMessage",
|
||||||
|
data: {
|
||||||
|
edit_id: i32,
|
||||||
|
content: String,
|
||||||
|
auth: String,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
##### Response
|
||||||
|
```rust
|
||||||
|
{
|
||||||
|
op: "EditPrivateMessage",
|
||||||
|
data: {
|
||||||
|
message: PrivateMessageView,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### HTTP
|
||||||
|
|
||||||
|
`PUT /private_message`
|
||||||
|
|
||||||
|
#### Delete Private Message
|
||||||
|
##### Request
|
||||||
|
```rust
|
||||||
|
{
|
||||||
|
op: "DeletePrivateMessage",
|
||||||
|
data: {
|
||||||
|
edit_id: i32,
|
||||||
|
deleted: bool,
|
||||||
|
auth: String,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
##### Response
|
||||||
|
```rust
|
||||||
|
{
|
||||||
|
op: "DeletePrivateMessage",
|
||||||
|
data: {
|
||||||
|
message: PrivateMessageView,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### HTTP
|
||||||
|
|
||||||
|
`POST /private_message/delete`
|
||||||
|
|
||||||
|
#### Mark Private Message as Read
|
||||||
|
##### Request
|
||||||
|
```rust
|
||||||
|
{
|
||||||
|
op: "MarkPrivateMessageAsRead",
|
||||||
|
data: {
|
||||||
|
edit_id: i32,
|
||||||
|
read: bool,
|
||||||
|
auth: String,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
##### Response
|
||||||
|
```rust
|
||||||
|
{
|
||||||
|
op: "MarkPrivateMessageAsRead",
|
||||||
|
data: {
|
||||||
|
message: PrivateMessageView,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### HTTP
|
||||||
|
|
||||||
|
`POST /private_message/mark_as_read`
|
||||||
|
|
||||||
#### Mark All As Read
|
#### Mark All As Read
|
||||||
|
|
||||||
Marks all user replies and mentions as read.
|
Marks all user replies and mentions as read.
|
||||||
|
|
|
@ -80,6 +80,50 @@ impl PrivateMessage {
|
||||||
.filter(ap_id.eq(object_id))
|
.filter(ap_id.eq(object_id))
|
||||||
.first::<Self>(conn)
|
.first::<Self>(conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_content(
|
||||||
|
conn: &PgConnection,
|
||||||
|
private_message_id: i32,
|
||||||
|
new_content: &str,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
use crate::schema::private_message::dsl::*;
|
||||||
|
diesel::update(private_message.find(private_message_id))
|
||||||
|
.set(content.eq(new_content))
|
||||||
|
.get_result::<Self>(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_deleted(
|
||||||
|
conn: &PgConnection,
|
||||||
|
private_message_id: i32,
|
||||||
|
new_deleted: bool,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
use crate::schema::private_message::dsl::*;
|
||||||
|
diesel::update(private_message.find(private_message_id))
|
||||||
|
.set(deleted.eq(new_deleted))
|
||||||
|
.get_result::<Self>(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_read(
|
||||||
|
conn: &PgConnection,
|
||||||
|
private_message_id: i32,
|
||||||
|
new_read: bool,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
use crate::schema::private_message::dsl::*;
|
||||||
|
diesel::update(private_message.find(private_message_id))
|
||||||
|
.set(read.eq(new_read))
|
||||||
|
.get_result::<Self>(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mark_all_as_read(conn: &PgConnection, for_recipient_id: i32) -> Result<Vec<Self>, Error> {
|
||||||
|
use crate::schema::private_message::dsl::*;
|
||||||
|
diesel::update(
|
||||||
|
private_message
|
||||||
|
.filter(recipient_id.eq(for_recipient_id))
|
||||||
|
.filter(read.eq(false)),
|
||||||
|
)
|
||||||
|
.set(read.eq(true))
|
||||||
|
.get_results::<Self>(conn)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -180,6 +224,10 @@ mod tests {
|
||||||
let read_private_message = PrivateMessage::read(&conn, inserted_private_message.id).unwrap();
|
let read_private_message = PrivateMessage::read(&conn, inserted_private_message.id).unwrap();
|
||||||
let updated_private_message =
|
let updated_private_message =
|
||||||
PrivateMessage::update(&conn, inserted_private_message.id, &private_message_form).unwrap();
|
PrivateMessage::update(&conn, inserted_private_message.id, &private_message_form).unwrap();
|
||||||
|
let deleted_private_message =
|
||||||
|
PrivateMessage::update_deleted(&conn, inserted_private_message.id, true).unwrap();
|
||||||
|
let marked_read_private_message =
|
||||||
|
PrivateMessage::update_read(&conn, inserted_private_message.id, true).unwrap();
|
||||||
let num_deleted = PrivateMessage::delete(&conn, inserted_private_message.id).unwrap();
|
let num_deleted = PrivateMessage::delete(&conn, inserted_private_message.id).unwrap();
|
||||||
User_::delete(&conn, inserted_creator.id).unwrap();
|
User_::delete(&conn, inserted_creator.id).unwrap();
|
||||||
User_::delete(&conn, inserted_recipient.id).unwrap();
|
User_::delete(&conn, inserted_recipient.id).unwrap();
|
||||||
|
@ -187,6 +235,8 @@ mod tests {
|
||||||
assert_eq!(expected_private_message, read_private_message);
|
assert_eq!(expected_private_message, read_private_message);
|
||||||
assert_eq!(expected_private_message, updated_private_message);
|
assert_eq!(expected_private_message, updated_private_message);
|
||||||
assert_eq!(expected_private_message, inserted_private_message);
|
assert_eq!(expected_private_message, inserted_private_message);
|
||||||
|
assert!(deleted_private_message.deleted);
|
||||||
|
assert!(marked_read_private_message.read);
|
||||||
assert_eq!(1, num_deleted);
|
assert_eq!(1, num_deleted);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,7 +110,7 @@ pub struct GetUserDetailsResponse {
|
||||||
moderates: Vec<CommunityModeratorView>,
|
moderates: Vec<CommunityModeratorView>,
|
||||||
comments: Vec<CommentView>,
|
comments: Vec<CommentView>,
|
||||||
posts: Vec<PostView>,
|
posts: Vec<PostView>,
|
||||||
admins: Vec<UserView>,
|
admins: Vec<UserView>, // TODO why is this necessary, just use GetSite
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
|
@ -216,9 +216,21 @@ pub struct CreatePrivateMessage {
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct EditPrivateMessage {
|
pub struct EditPrivateMessage {
|
||||||
edit_id: i32,
|
edit_id: i32,
|
||||||
content: Option<String>,
|
content: String,
|
||||||
deleted: Option<bool>,
|
auth: String,
|
||||||
read: Option<bool>,
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct DeletePrivateMessage {
|
||||||
|
edit_id: i32,
|
||||||
|
deleted: bool,
|
||||||
|
auth: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct MarkPrivateMessageAsRead {
|
||||||
|
edit_id: i32,
|
||||||
|
read: bool,
|
||||||
auth: String,
|
auth: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -974,37 +986,11 @@ impl Perform for Oper<MarkAllAsRead> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// messages
|
// Mark all private_messages as read
|
||||||
let messages = blocking(pool, move |conn| {
|
let update_pm = move |conn: &'_ _| PrivateMessage::mark_all_as_read(conn, user_id);
|
||||||
PrivateMessageQueryBuilder::create(conn, user_id)
|
|
||||||
.page(1)
|
|
||||||
.limit(999)
|
|
||||||
.unread_only(true)
|
|
||||||
.list()
|
|
||||||
})
|
|
||||||
.await??;
|
|
||||||
|
|
||||||
// TODO: this should probably be a bulk operation
|
|
||||||
for message in &messages {
|
|
||||||
let private_message_form = PrivateMessageForm {
|
|
||||||
content: message.to_owned().content,
|
|
||||||
creator_id: message.to_owned().creator_id,
|
|
||||||
recipient_id: message.to_owned().recipient_id,
|
|
||||||
deleted: None,
|
|
||||||
read: Some(true),
|
|
||||||
updated: None,
|
|
||||||
ap_id: message.to_owned().ap_id,
|
|
||||||
local: message.local,
|
|
||||||
published: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let message_id = message.id;
|
|
||||||
let update_pm =
|
|
||||||
move |conn: &'_ _| PrivateMessage::update(conn, message_id, &private_message_form);
|
|
||||||
if blocking(pool, update_pm).await?.is_err() {
|
if blocking(pool, update_pm).await?.is_err() {
|
||||||
return Err(APIError::err("couldnt_update_private_message").into());
|
return Err(APIError::err("couldnt_update_private_message").into());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Ok(GetRepliesResponse { replies: vec![] })
|
Ok(GetRepliesResponse { replies: vec![] })
|
||||||
}
|
}
|
||||||
|
@ -1293,59 +1279,25 @@ impl Perform for Oper<EditPrivateMessage> {
|
||||||
|
|
||||||
let user_id = claims.id;
|
let user_id = claims.id;
|
||||||
|
|
||||||
let edit_id = data.edit_id;
|
|
||||||
let orig_private_message =
|
|
||||||
blocking(pool, move |conn| PrivateMessage::read(conn, edit_id)).await??;
|
|
||||||
|
|
||||||
// Check for a site ban
|
// Check for a site ban
|
||||||
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
||||||
if user.banned {
|
if user.banned {
|
||||||
return Err(APIError::err("site_ban").into());
|
return Err(APIError::err("site_ban").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check to make sure they are the creator (or the recipient marking as read
|
// Checking permissions
|
||||||
if !(data.read.is_some() && orig_private_message.recipient_id.eq(&user_id)
|
let edit_id = data.edit_id;
|
||||||
|| orig_private_message.creator_id.eq(&user_id))
|
let orig_private_message =
|
||||||
{
|
blocking(pool, move |conn| PrivateMessage::read(conn, edit_id)).await??;
|
||||||
|
if user_id != orig_private_message.creator_id {
|
||||||
return Err(APIError::err("no_private_message_edit_allowed").into());
|
return Err(APIError::err("no_private_message_edit_allowed").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let content_slurs_removed = match &data.content {
|
// Doing the update
|
||||||
Some(content) => remove_slurs(content),
|
let content_slurs_removed = remove_slurs(&data.content);
|
||||||
None => orig_private_message.content.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let private_message_form = {
|
|
||||||
if data.read.is_some() {
|
|
||||||
PrivateMessageForm {
|
|
||||||
content: orig_private_message.content.to_owned(),
|
|
||||||
creator_id: orig_private_message.creator_id,
|
|
||||||
recipient_id: orig_private_message.recipient_id,
|
|
||||||
read: data.read.to_owned(),
|
|
||||||
updated: orig_private_message.updated,
|
|
||||||
deleted: Some(orig_private_message.deleted),
|
|
||||||
ap_id: orig_private_message.ap_id,
|
|
||||||
local: orig_private_message.local,
|
|
||||||
published: None,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
PrivateMessageForm {
|
|
||||||
content: content_slurs_removed,
|
|
||||||
creator_id: orig_private_message.creator_id,
|
|
||||||
recipient_id: orig_private_message.recipient_id,
|
|
||||||
deleted: data.deleted.to_owned(),
|
|
||||||
read: Some(orig_private_message.read),
|
|
||||||
updated: Some(naive_now()),
|
|
||||||
ap_id: orig_private_message.ap_id,
|
|
||||||
local: orig_private_message.local,
|
|
||||||
published: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let edit_id = data.edit_id;
|
let edit_id = data.edit_id;
|
||||||
let updated_private_message = match blocking(pool, move |conn| {
|
let updated_private_message = match blocking(pool, move |conn| {
|
||||||
PrivateMessage::update(conn, edit_id, &private_message_form)
|
PrivateMessage::update_content(conn, edit_id, &content_slurs_removed)
|
||||||
})
|
})
|
||||||
.await?
|
.await?
|
||||||
{
|
{
|
||||||
|
@ -1353,9 +1305,76 @@ impl Perform for Oper<EditPrivateMessage> {
|
||||||
Err(_e) => return Err(APIError::err("couldnt_update_private_message").into()),
|
Err(_e) => return Err(APIError::err("couldnt_update_private_message").into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
if data.read.is_none() {
|
// Send the apub update
|
||||||
if let Some(deleted) = data.deleted.to_owned() {
|
updated_private_message
|
||||||
if deleted {
|
.send_update(&user, &self.client, pool)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let edit_id = data.edit_id;
|
||||||
|
let message = blocking(pool, move |conn| PrivateMessageView::read(conn, edit_id)).await??;
|
||||||
|
let recipient_id = message.recipient_id;
|
||||||
|
|
||||||
|
let res = PrivateMessageResponse { message };
|
||||||
|
|
||||||
|
if let Some(ws) = websocket_info {
|
||||||
|
ws.chatserver.do_send(SendUserRoomMessage {
|
||||||
|
op: UserOperation::EditPrivateMessage,
|
||||||
|
response: res.clone(),
|
||||||
|
recipient_id,
|
||||||
|
my_id: ws.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
|
impl Perform for Oper<DeletePrivateMessage> {
|
||||||
|
type Response = PrivateMessageResponse;
|
||||||
|
|
||||||
|
async fn perform(
|
||||||
|
&self,
|
||||||
|
pool: &DbPool,
|
||||||
|
websocket_info: Option<WebsocketInfo>,
|
||||||
|
) -> Result<PrivateMessageResponse, LemmyError> {
|
||||||
|
let data: &DeletePrivateMessage = &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;
|
||||||
|
|
||||||
|
// 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checking permissions
|
||||||
|
let edit_id = data.edit_id;
|
||||||
|
let orig_private_message =
|
||||||
|
blocking(pool, move |conn| PrivateMessage::read(conn, edit_id)).await??;
|
||||||
|
if user_id != orig_private_message.creator_id {
|
||||||
|
return Err(APIError::err("no_private_message_edit_allowed").into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Doing the update
|
||||||
|
let edit_id = data.edit_id;
|
||||||
|
let deleted = data.deleted;
|
||||||
|
let updated_private_message = match blocking(pool, move |conn| {
|
||||||
|
PrivateMessage::update_deleted(conn, edit_id, deleted)
|
||||||
|
})
|
||||||
|
.await?
|
||||||
|
{
|
||||||
|
Ok(private_message) => private_message,
|
||||||
|
Err(_e) => return Err(APIError::err("couldnt_update_private_message").into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Send the apub update
|
||||||
|
if data.deleted {
|
||||||
updated_private_message
|
updated_private_message
|
||||||
.send_delete(&user, &self.client, pool)
|
.send_delete(&user, &self.client, pool)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -1364,27 +1383,83 @@ impl Perform for Oper<EditPrivateMessage> {
|
||||||
.send_undo_delete(&user, &self.client, pool)
|
.send_undo_delete(&user, &self.client, pool)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
updated_private_message
|
|
||||||
.send_update(&user, &self.client, pool)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
updated_private_message
|
|
||||||
.send_update(&user, &self.client, pool)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let edit_id = data.edit_id;
|
let edit_id = data.edit_id;
|
||||||
let message = blocking(pool, move |conn| PrivateMessageView::read(conn, edit_id)).await??;
|
let message = blocking(pool, move |conn| PrivateMessageView::read(conn, edit_id)).await??;
|
||||||
|
let recipient_id = message.recipient_id;
|
||||||
|
|
||||||
let res = PrivateMessageResponse { message };
|
let res = PrivateMessageResponse { message };
|
||||||
|
|
||||||
if let Some(ws) = websocket_info {
|
if let Some(ws) = websocket_info {
|
||||||
ws.chatserver.do_send(SendUserRoomMessage {
|
ws.chatserver.do_send(SendUserRoomMessage {
|
||||||
op: UserOperation::EditPrivateMessage,
|
op: UserOperation::DeletePrivateMessage,
|
||||||
response: res.clone(),
|
response: res.clone(),
|
||||||
recipient_id: orig_private_message.recipient_id,
|
recipient_id,
|
||||||
|
my_id: ws.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
|
impl Perform for Oper<MarkPrivateMessageAsRead> {
|
||||||
|
type Response = PrivateMessageResponse;
|
||||||
|
|
||||||
|
async fn perform(
|
||||||
|
&self,
|
||||||
|
pool: &DbPool,
|
||||||
|
websocket_info: Option<WebsocketInfo>,
|
||||||
|
) -> Result<PrivateMessageResponse, LemmyError> {
|
||||||
|
let data: &MarkPrivateMessageAsRead = &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;
|
||||||
|
|
||||||
|
// 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checking permissions
|
||||||
|
let edit_id = data.edit_id;
|
||||||
|
let orig_private_message =
|
||||||
|
blocking(pool, move |conn| PrivateMessage::read(conn, edit_id)).await??;
|
||||||
|
if user_id != orig_private_message.recipient_id {
|
||||||
|
return Err(APIError::err("couldnt_update_private_message").into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Doing the update
|
||||||
|
let edit_id = data.edit_id;
|
||||||
|
let read = data.read;
|
||||||
|
match blocking(pool, move |conn| {
|
||||||
|
PrivateMessage::update_read(conn, edit_id, read)
|
||||||
|
})
|
||||||
|
.await?
|
||||||
|
{
|
||||||
|
Ok(private_message) => private_message,
|
||||||
|
Err(_e) => return Err(APIError::err("couldnt_update_private_message").into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
// No need to send an apub update
|
||||||
|
|
||||||
|
let edit_id = data.edit_id;
|
||||||
|
let message = blocking(pool, move |conn| PrivateMessageView::read(conn, edit_id)).await??;
|
||||||
|
let recipient_id = message.recipient_id;
|
||||||
|
|
||||||
|
let res = PrivateMessageResponse { message };
|
||||||
|
|
||||||
|
if let Some(ws) = websocket_info {
|
||||||
|
ws.chatserver.do_send(SendUserRoomMessage {
|
||||||
|
op: UserOperation::MarkPrivateMessageAsRead,
|
||||||
|
response: res.clone(),
|
||||||
|
recipient_id,
|
||||||
my_id: ws.id,
|
my_id: ws.id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,7 +90,15 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimit) {
|
||||||
.wrap(rate_limit.message())
|
.wrap(rate_limit.message())
|
||||||
.route("/list", web::get().to(route_get::<GetPrivateMessages>))
|
.route("/list", web::get().to(route_get::<GetPrivateMessages>))
|
||||||
.route("", web::post().to(route_post::<CreatePrivateMessage>))
|
.route("", web::post().to(route_post::<CreatePrivateMessage>))
|
||||||
.route("", web::put().to(route_post::<EditPrivateMessage>)),
|
.route("", web::put().to(route_post::<EditPrivateMessage>))
|
||||||
|
.route(
|
||||||
|
"/delete",
|
||||||
|
web::post().to(route_post::<DeletePrivateMessage>),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/mark_as_read",
|
||||||
|
web::post().to(route_post::<MarkPrivateMessageAsRead>),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
// User
|
// User
|
||||||
.service(
|
.service(
|
||||||
|
|
|
@ -59,6 +59,8 @@ pub enum UserOperation {
|
||||||
PasswordChange,
|
PasswordChange,
|
||||||
CreatePrivateMessage,
|
CreatePrivateMessage,
|
||||||
EditPrivateMessage,
|
EditPrivateMessage,
|
||||||
|
DeletePrivateMessage,
|
||||||
|
MarkPrivateMessageAsRead,
|
||||||
GetPrivateMessages,
|
GetPrivateMessages,
|
||||||
UserJoin,
|
UserJoin,
|
||||||
GetComments,
|
GetComments,
|
||||||
|
|
|
@ -448,13 +448,21 @@ impl ChatServer {
|
||||||
UserOperation::DeleteAccount => do_user_operation::<DeleteAccount>(args).await,
|
UserOperation::DeleteAccount => do_user_operation::<DeleteAccount>(args).await,
|
||||||
UserOperation::PasswordReset => do_user_operation::<PasswordReset>(args).await,
|
UserOperation::PasswordReset => do_user_operation::<PasswordReset>(args).await,
|
||||||
UserOperation::PasswordChange => do_user_operation::<PasswordChange>(args).await,
|
UserOperation::PasswordChange => do_user_operation::<PasswordChange>(args).await,
|
||||||
|
UserOperation::UserJoin => do_user_operation::<UserJoin>(args).await,
|
||||||
|
UserOperation::SaveUserSettings => do_user_operation::<SaveUserSettings>(args).await,
|
||||||
|
|
||||||
|
// Private Message ops
|
||||||
UserOperation::CreatePrivateMessage => {
|
UserOperation::CreatePrivateMessage => {
|
||||||
do_user_operation::<CreatePrivateMessage>(args).await
|
do_user_operation::<CreatePrivateMessage>(args).await
|
||||||
}
|
}
|
||||||
UserOperation::EditPrivateMessage => do_user_operation::<EditPrivateMessage>(args).await,
|
UserOperation::EditPrivateMessage => do_user_operation::<EditPrivateMessage>(args).await,
|
||||||
|
UserOperation::DeletePrivateMessage => {
|
||||||
|
do_user_operation::<DeletePrivateMessage>(args).await
|
||||||
|
}
|
||||||
|
UserOperation::MarkPrivateMessageAsRead => {
|
||||||
|
do_user_operation::<MarkPrivateMessageAsRead>(args).await
|
||||||
|
}
|
||||||
UserOperation::GetPrivateMessages => do_user_operation::<GetPrivateMessages>(args).await,
|
UserOperation::GetPrivateMessages => do_user_operation::<GetPrivateMessages>(args).await,
|
||||||
UserOperation::UserJoin => do_user_operation::<UserJoin>(args).await,
|
|
||||||
UserOperation::SaveUserSettings => do_user_operation::<SaveUserSettings>(args).await,
|
|
||||||
|
|
||||||
// Site ops
|
// Site ops
|
||||||
UserOperation::GetModlog => do_user_operation::<GetModlog>(args).await,
|
UserOperation::GetModlog => do_user_operation::<GetModlog>(args).await,
|
||||||
|
|
15
ui/src/api_tests/api.spec.ts
vendored
15
ui/src/api_tests/api.spec.ts
vendored
|
@ -9,17 +9,16 @@ import {
|
||||||
FollowCommunityForm,
|
FollowCommunityForm,
|
||||||
CommunityResponse,
|
CommunityResponse,
|
||||||
GetFollowedCommunitiesResponse,
|
GetFollowedCommunitiesResponse,
|
||||||
GetPostForm,
|
|
||||||
GetPostResponse,
|
GetPostResponse,
|
||||||
CommentForm,
|
CommentForm,
|
||||||
CommentResponse,
|
CommentResponse,
|
||||||
CommunityForm,
|
CommunityForm,
|
||||||
GetCommunityForm,
|
|
||||||
GetCommunityResponse,
|
GetCommunityResponse,
|
||||||
CommentLikeForm,
|
CommentLikeForm,
|
||||||
CreatePostLikeForm,
|
CreatePostLikeForm,
|
||||||
PrivateMessageForm,
|
PrivateMessageForm,
|
||||||
EditPrivateMessageForm,
|
EditPrivateMessageForm,
|
||||||
|
DeletePrivateMessageForm,
|
||||||
PrivateMessageResponse,
|
PrivateMessageResponse,
|
||||||
PrivateMessagesResponse,
|
PrivateMessagesResponse,
|
||||||
GetUserMentionsResponse,
|
GetUserMentionsResponse,
|
||||||
|
@ -1149,16 +1148,16 @@ describe('main', () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
// lemmy alpha deletes the private message
|
// lemmy alpha deletes the private message
|
||||||
let deletePrivateMessageForm: EditPrivateMessageForm = {
|
let deletePrivateMessageForm: DeletePrivateMessageForm = {
|
||||||
deleted: true,
|
deleted: true,
|
||||||
edit_id: createRes.message.id,
|
edit_id: createRes.message.id,
|
||||||
auth: lemmyAlphaAuth,
|
auth: lemmyAlphaAuth,
|
||||||
};
|
};
|
||||||
|
|
||||||
let deleteRes: PrivateMessageResponse = await fetch(
|
let deleteRes: PrivateMessageResponse = await fetch(
|
||||||
`${lemmyAlphaApiUrl}/private_message`,
|
`${lemmyAlphaApiUrl}/private_message/delete`,
|
||||||
{
|
{
|
||||||
method: 'PUT',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
|
@ -1182,16 +1181,16 @@ describe('main', () => {
|
||||||
expect(getPrivateMessagesDeletedRes.messages.length).toBe(0);
|
expect(getPrivateMessagesDeletedRes.messages.length).toBe(0);
|
||||||
|
|
||||||
// lemmy alpha undeletes the private message
|
// lemmy alpha undeletes the private message
|
||||||
let undeletePrivateMessageForm: EditPrivateMessageForm = {
|
let undeletePrivateMessageForm: DeletePrivateMessageForm = {
|
||||||
deleted: false,
|
deleted: false,
|
||||||
edit_id: createRes.message.id,
|
edit_id: createRes.message.id,
|
||||||
auth: lemmyAlphaAuth,
|
auth: lemmyAlphaAuth,
|
||||||
};
|
};
|
||||||
|
|
||||||
let undeleteRes: PrivateMessageResponse = await fetch(
|
let undeleteRes: PrivateMessageResponse = await fetch(
|
||||||
`${lemmyAlphaApiUrl}/private_message`,
|
`${lemmyAlphaApiUrl}/private_message/delete`,
|
||||||
{
|
{
|
||||||
method: 'PUT',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
|
|
24
ui/src/components/inbox.tsx
vendored
24
ui/src/components/inbox.tsx
vendored
|
@ -446,9 +446,30 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
let found: PrivateMessageI = this.state.messages.find(
|
let found: PrivateMessageI = this.state.messages.find(
|
||||||
m => m.id === data.message.id
|
m => m.id === data.message.id
|
||||||
);
|
);
|
||||||
|
if (found) {
|
||||||
found.content = data.message.content;
|
found.content = data.message.content;
|
||||||
found.updated = data.message.updated;
|
found.updated = data.message.updated;
|
||||||
|
}
|
||||||
|
this.setState(this.state);
|
||||||
|
} else if (res.op == UserOperation.DeletePrivateMessage) {
|
||||||
|
let data = res.data as PrivateMessageResponse;
|
||||||
|
let found: PrivateMessageI = this.state.messages.find(
|
||||||
|
m => m.id === data.message.id
|
||||||
|
);
|
||||||
|
if (found) {
|
||||||
found.deleted = data.message.deleted;
|
found.deleted = data.message.deleted;
|
||||||
|
found.updated = data.message.updated;
|
||||||
|
}
|
||||||
|
this.setState(this.state);
|
||||||
|
} else if (res.op == UserOperation.MarkPrivateMessageAsRead) {
|
||||||
|
let data = res.data as PrivateMessageResponse;
|
||||||
|
let found: PrivateMessageI = this.state.messages.find(
|
||||||
|
m => m.id === data.message.id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
found.updated = data.message.updated;
|
||||||
|
|
||||||
// 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.message.read) {
|
if (this.state.unreadOrAll == UnreadOrAll.Unread && data.message.read) {
|
||||||
this.state.messages = this.state.messages.filter(
|
this.state.messages = this.state.messages.filter(
|
||||||
|
@ -458,10 +479,9 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
let found = this.state.messages.find(c => c.id == data.message.id);
|
let found = this.state.messages.find(c => c.id == data.message.id);
|
||||||
found.read = data.message.read;
|
found.read = data.message.read;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
this.sendUnreadCount();
|
this.sendUnreadCount();
|
||||||
window.scrollTo(0, 0);
|
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
setupTippy();
|
|
||||||
} 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) {
|
||||||
|
|
6
ui/src/components/private-message-form.tsx
vendored
6
ui/src/components/private-message-form.tsx
vendored
|
@ -263,7 +263,11 @@ export class PrivateMessageForm extends Component<
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
return;
|
return;
|
||||||
} else if (res.op == UserOperation.EditPrivateMessage) {
|
} else if (
|
||||||
|
res.op == UserOperation.EditPrivateMessage ||
|
||||||
|
res.op == UserOperation.DeletePrivateMessage ||
|
||||||
|
res.op == UserOperation.MarkPrivateMessageAsRead
|
||||||
|
) {
|
||||||
let data = res.data as PrivateMessageResponse;
|
let data = res.data as PrivateMessageResponse;
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
this.props.onEdit(data.message);
|
this.props.onEdit(data.message);
|
||||||
|
|
11
ui/src/components/private-message.tsx
vendored
11
ui/src/components/private-message.tsx
vendored
|
@ -2,7 +2,8 @@ import { Component, linkEvent } from 'inferno';
|
||||||
import { Link } from 'inferno-router';
|
import { Link } from 'inferno-router';
|
||||||
import {
|
import {
|
||||||
PrivateMessage as PrivateMessageI,
|
PrivateMessage as PrivateMessageI,
|
||||||
EditPrivateMessageForm,
|
DeletePrivateMessageForm,
|
||||||
|
MarkPrivateMessageAsReadForm,
|
||||||
} from '../interfaces';
|
} from '../interfaces';
|
||||||
import { WebSocketService, UserService } from '../services';
|
import { WebSocketService, UserService } from '../services';
|
||||||
import { mdToHtml, pictrsAvatarThumbnail, showAvatars, toast } from '../utils';
|
import { mdToHtml, pictrsAvatarThumbnail, showAvatars, toast } from '../utils';
|
||||||
|
@ -243,11 +244,11 @@ export class PrivateMessage extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDeleteClick(i: PrivateMessage) {
|
handleDeleteClick(i: PrivateMessage) {
|
||||||
let form: EditPrivateMessageForm = {
|
let form: DeletePrivateMessageForm = {
|
||||||
edit_id: i.props.privateMessage.id,
|
edit_id: i.props.privateMessage.id,
|
||||||
deleted: !i.props.privateMessage.deleted,
|
deleted: !i.props.privateMessage.deleted,
|
||||||
};
|
};
|
||||||
WebSocketService.Instance.editPrivateMessage(form);
|
WebSocketService.Instance.deletePrivateMessage(form);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleReplyCancel() {
|
handleReplyCancel() {
|
||||||
|
@ -257,11 +258,11 @@ export class PrivateMessage extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMarkRead(i: PrivateMessage) {
|
handleMarkRead(i: PrivateMessage) {
|
||||||
let form: EditPrivateMessageForm = {
|
let form: MarkPrivateMessageAsReadForm = {
|
||||||
edit_id: i.props.privateMessage.id,
|
edit_id: i.props.privateMessage.id,
|
||||||
read: !i.props.privateMessage.read,
|
read: !i.props.privateMessage.read,
|
||||||
};
|
};
|
||||||
WebSocketService.Instance.editPrivateMessage(form);
|
WebSocketService.Instance.markPrivateMessageAsRead(form);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMessageCollapse(i: PrivateMessage) {
|
handleMessageCollapse(i: PrivateMessage) {
|
||||||
|
|
21
ui/src/interfaces.ts
vendored
21
ui/src/interfaces.ts
vendored
|
@ -40,6 +40,8 @@ export enum UserOperation {
|
||||||
PasswordChange,
|
PasswordChange,
|
||||||
CreatePrivateMessage,
|
CreatePrivateMessage,
|
||||||
EditPrivateMessage,
|
EditPrivateMessage,
|
||||||
|
DeletePrivateMessage,
|
||||||
|
MarkPrivateMessageAsRead,
|
||||||
GetPrivateMessages,
|
GetPrivateMessages,
|
||||||
UserJoin,
|
UserJoin,
|
||||||
GetComments,
|
GetComments,
|
||||||
|
@ -834,9 +836,19 @@ export interface PrivateMessageFormParams {
|
||||||
|
|
||||||
export interface EditPrivateMessageForm {
|
export interface EditPrivateMessageForm {
|
||||||
edit_id: number;
|
edit_id: number;
|
||||||
content?: string;
|
content: string;
|
||||||
deleted?: boolean;
|
auth?: string;
|
||||||
read?: boolean;
|
}
|
||||||
|
|
||||||
|
export interface DeletePrivateMessageForm {
|
||||||
|
edit_id: number;
|
||||||
|
deleted: boolean;
|
||||||
|
auth?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MarkPrivateMessageAsReadForm {
|
||||||
|
edit_id: number;
|
||||||
|
read: boolean;
|
||||||
auth?: string;
|
auth?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -864,7 +876,6 @@ export interface UserJoinResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MessageType =
|
export type MessageType =
|
||||||
| EditPrivateMessageForm
|
|
||||||
| LoginForm
|
| LoginForm
|
||||||
| RegisterForm
|
| RegisterForm
|
||||||
| CommunityForm
|
| CommunityForm
|
||||||
|
@ -900,6 +911,8 @@ export type MessageType =
|
||||||
| PasswordChangeForm
|
| PasswordChangeForm
|
||||||
| PrivateMessageForm
|
| PrivateMessageForm
|
||||||
| EditPrivateMessageForm
|
| EditPrivateMessageForm
|
||||||
|
| DeletePrivateMessageForm
|
||||||
|
| MarkPrivateMessageAsReadForm
|
||||||
| GetPrivateMessagesForm
|
| GetPrivateMessagesForm
|
||||||
| SiteConfigForm;
|
| SiteConfigForm;
|
||||||
|
|
||||||
|
|
14
ui/src/services/WebSocketService.ts
vendored
14
ui/src/services/WebSocketService.ts
vendored
|
@ -36,6 +36,8 @@ import {
|
||||||
PasswordChangeForm,
|
PasswordChangeForm,
|
||||||
PrivateMessageForm,
|
PrivateMessageForm,
|
||||||
EditPrivateMessageForm,
|
EditPrivateMessageForm,
|
||||||
|
DeletePrivateMessageForm,
|
||||||
|
MarkPrivateMessageAsReadForm,
|
||||||
GetPrivateMessagesForm,
|
GetPrivateMessagesForm,
|
||||||
GetCommentsForm,
|
GetCommentsForm,
|
||||||
UserJoinForm,
|
UserJoinForm,
|
||||||
|
@ -315,6 +317,18 @@ export class WebSocketService {
|
||||||
this.ws.send(this.wsSendWrapper(UserOperation.EditPrivateMessage, form));
|
this.ws.send(this.wsSendWrapper(UserOperation.EditPrivateMessage, form));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public deletePrivateMessage(form: DeletePrivateMessageForm) {
|
||||||
|
this.setAuth(form);
|
||||||
|
this.ws.send(this.wsSendWrapper(UserOperation.DeletePrivateMessage, form));
|
||||||
|
}
|
||||||
|
|
||||||
|
public markPrivateMessageAsRead(form: MarkPrivateMessageAsReadForm) {
|
||||||
|
this.setAuth(form);
|
||||||
|
this.ws.send(
|
||||||
|
this.wsSendWrapper(UserOperation.MarkPrivateMessageAsRead, form)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public getPrivateMessages(form: GetPrivateMessagesForm) {
|
public getPrivateMessages(form: GetPrivateMessagesForm) {
|
||||||
this.setAuth(form);
|
this.setAuth(form);
|
||||||
this.ws.send(this.wsSendWrapper(UserOperation.GetPrivateMessages, form));
|
this.ws.send(this.wsSendWrapper(UserOperation.GetPrivateMessages, form));
|
||||||
|
|
Loading…
Reference in a new issue