From 3b6fb20e111336388d99932c8866bbdd02b3ad12 Mon Sep 17 00:00:00 2001 From: Simon Laux Date: Sun, 12 Jan 2025 04:10:51 +0100 Subject: [PATCH 1/3] feat: jsonrpc: add `create_draft`, `draft_set_text`, `draft_set_subject`, `draft_set_quoted_message`, `draft_set_quoted_text` --- deltachat-jsonrpc/src/api.rs | 129 ++++++++++++++++++++- deltachat-jsonrpc/src/api/types/message.rs | 11 ++ 2 files changed, 139 insertions(+), 1 deletion(-) diff --git a/deltachat-jsonrpc/src/api.rs b/deltachat-jsonrpc/src/api.rs index cb6260d1cc..ddb9ee337f 100644 --- a/deltachat-jsonrpc/src/api.rs +++ b/deltachat-jsonrpc/src/api.rs @@ -49,7 +49,7 @@ use types::chat::FullChat; use types::contact::{ContactObject, VcardContact}; use types::events::Event; use types::http::HttpResponse; -use types::message::{MessageData, MessageObject, MessageReadReceipt}; +use types::message::{MessageData, MessageObject, MessageReadReceipt, QuotedText}; use types::provider_info::ProviderInfo; use types::reactions::JSONRPCReactions; use types::webxdc::WebxdcMessageInfo; @@ -2022,6 +2022,133 @@ impl CommandApi { } } + /// Create a new draft (overwriting existing draft) + /// + /// You can modify some fields of an existing draft while keeping it's message id (important to keep webxdc status updates) with the following methods: + /// - [Self::draft_set_text] + /// - [Self::draft_set_quoted_message] + /// - [Self::draft_set_quoted_text] + /// For other actions like changing the view type or file attachment you have to recreate the draft. + /// + /// You can send the draft with [Self::send_draft] + async fn create_draft(&self, account_id: u32, chat_id: u32, data: MessageData) -> Result { + let ctx = self.get_context(account_id).await?; + let mut message = data + .create_message(&ctx) + .await + .context("Failed to create message")?; + + ChatId::new(chat_id) + .set_draft(&ctx, Some(&mut message)) + .await + .context("Failed to set draft message")?; + Ok(message.get_id().to_u32()) + } + + /// set text of draft + async fn draft_set_text(&self, account_id: u32, msg_id: u32, text: String) -> Result<()> { + let ctx = self.get_context(account_id).await?; + let mut message = message::Message::load_from_db(&ctx, MsgId::new(msg_id)).await?; + + if message.get_state() != deltachat::message::MessageState::OutDraft { + bail!("provided message is not a draft"); + } + + message.set_text(text); + message + .get_chat_id() + .set_draft(&ctx, Some(&mut message)) + .await?; + + Ok(()) + } + + /// set (email) subject of draft + async fn draft_set_subject(&self, account_id: u32, msg_id: u32, subject: String) -> Result<()> { + let ctx = self.get_context(account_id).await?; + let mut message = message::Message::load_from_db(&ctx, MsgId::new(msg_id)).await?; + + if message.get_state() != deltachat::message::MessageState::OutDraft { + bail!("provided message is not a draft"); + } + + message.set_subject(subject); + message + .get_chat_id() + .set_draft(&ctx, Some(&mut message)) + .await?; + + Ok(()) + } + + /// set quoted message id of draft + async fn draft_set_quoted_message( + &self, + account_id: u32, + msg_id: u32, + quoted_message_id: Option, + ) -> Result<()> { + let ctx = self.get_context(account_id).await?; + let mut message = message::Message::load_from_db(&ctx, MsgId::new(msg_id)).await?; + + if message.get_state() != deltachat::message::MessageState::OutDraft { + bail!("provided message is not a draft"); + } + + let message_to_qoute = match quoted_message_id { + Some(msg_id) => Some(message::Message::load_from_db(&ctx, MsgId::new(msg_id)).await?), + None => None, + }; + + message.set_quote(&ctx, message_to_qoute.as_ref()).await?; + message + .get_chat_id() + .set_draft(&ctx, Some(&mut message)) + .await?; + + Ok(()) + } + + /// set quoted text of draft + async fn draft_set_quoted_text( + &self, + account_id: u32, + msg_id: u32, + quoted_text: Option, + ) -> Result<()> { + let ctx = self.get_context(account_id).await?; + let mut message = message::Message::load_from_db(&ctx, MsgId::new(msg_id)).await?; + + if message.get_state() != deltachat::message::MessageState::OutDraft { + bail!("provided message is not a draft"); + } + + message.set_quote_text(quoted_text.map(|qt| (qt.text, qt.protect))); + + message + .get_chat_id() + .set_draft(&ctx, Some(&mut message)) + .await?; + + Ok(()) + } + + /// send draft + async fn send_draft(&self, account_id: u32, msg_id: u32) -> Result { + // uses message id instead of chat id to force ui to have the right message id / know about the draft - reduce the chance of undefined behaviour + let ctx = self.get_context(account_id).await?; + let mut draft = message::Message::load_from_db(&ctx, MsgId::new(msg_id)).await?; + + if draft.get_state() != deltachat::message::MessageState::OutDraft { + bail!("provided message is not a draft"); + } + + let chat = draft.get_chat_id(); + let msg_id = chat::send_msg(&ctx, chat, &mut draft).await?.to_u32(); + Ok(msg_id) + } + + async fn send_videochat_invitation(&self, account_id: u32, chat_id: u32) -> Result { let ctx = self.get_context(account_id).await?; chat::send_videochat_invitation(&ctx, ChatId::new(chat_id)) diff --git a/deltachat-jsonrpc/src/api/types/message.rs b/deltachat-jsonrpc/src/api/types/message.rs index c2ca8fe7b4..31d858006f 100644 --- a/deltachat-jsonrpc/src/api/types/message.rs +++ b/deltachat-jsonrpc/src/api/types/message.rs @@ -708,3 +708,14 @@ impl From for EphemeralTimer { } } } + + +#[derive(Deserialize, Serialize, TypeDef, schemars::JsonSchema)] +#[serde(rename_all = "camelCase")] +pub struct QuotedText { + /// Text shown in the Quote + pub text: String, + /// protect specifies whether text should only be sent encrypted. + /// If it should, but the message is unencrypted, text is replaced with "...". + pub protect: bool +} \ No newline at end of file From cbc79cdcc56cafab4f1ca8960a61e7765daf457d Mon Sep 17 00:00:00 2001 From: Simon Laux Date: Sun, 12 Jan 2025 04:11:37 +0100 Subject: [PATCH 2/3] refactor: jsonrpc: deprecate `misc_set_draft ` and`misc_send_draft` --- deltachat-jsonrpc/src/api.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/deltachat-jsonrpc/src/api.rs b/deltachat-jsonrpc/src/api.rs index ddb9ee337f..6f1b70a7b7 100644 --- a/deltachat-jsonrpc/src/api.rs +++ b/deltachat-jsonrpc/src/api.rs @@ -2322,6 +2322,7 @@ impl CommandApi { // the better version should support: // - changing viewtype to enable/disable compression // - keeping same message id as long as attachment does not change for webxdc messages + /// @deprecated use [Self::send_draft] instead async fn misc_set_draft( &self, account_id: u32, @@ -2363,6 +2364,7 @@ impl CommandApi { } // send the chat's current set draft + /// @deprecated use [Self::send_draft] instead async fn misc_send_draft(&self, account_id: u32, chat_id: u32) -> Result { let ctx = self.get_context(account_id).await?; if let Some(draft) = ChatId::new(chat_id).get_draft(&ctx).await? { From 0cbc1c3eb888ca31f0e0e5d3f93ecde5eae3db1e Mon Sep 17 00:00:00 2001 From: Simon Laux Date: Sun, 12 Jan 2025 04:16:57 +0100 Subject: [PATCH 3/3] cargo fmt --- deltachat-jsonrpc/src/api.rs | 1 - deltachat-jsonrpc/src/api/types/message.rs | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/deltachat-jsonrpc/src/api.rs b/deltachat-jsonrpc/src/api.rs index 6f1b70a7b7..ece7637624 100644 --- a/deltachat-jsonrpc/src/api.rs +++ b/deltachat-jsonrpc/src/api.rs @@ -2148,7 +2148,6 @@ impl CommandApi { Ok(msg_id) } - async fn send_videochat_invitation(&self, account_id: u32, chat_id: u32) -> Result { let ctx = self.get_context(account_id).await?; chat::send_videochat_invitation(&ctx, ChatId::new(chat_id)) diff --git a/deltachat-jsonrpc/src/api/types/message.rs b/deltachat-jsonrpc/src/api/types/message.rs index 31d858006f..588280db7f 100644 --- a/deltachat-jsonrpc/src/api/types/message.rs +++ b/deltachat-jsonrpc/src/api/types/message.rs @@ -709,7 +709,6 @@ impl From for EphemeralTimer { } } - #[derive(Deserialize, Serialize, TypeDef, schemars::JsonSchema)] #[serde(rename_all = "camelCase")] pub struct QuotedText { @@ -717,5 +716,5 @@ pub struct QuotedText { pub text: String, /// protect specifies whether text should only be sent encrypted. /// If it should, but the message is unencrypted, text is replaced with "...". - pub protect: bool -} \ No newline at end of file + pub protect: bool, +}