Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

simon/better draft api in jsonrpc #6426

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 129 additions & 1 deletion deltachat-jsonrpc/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -2022,6 +2022,132 @@ 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<u32> {
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))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bit ugly that this function sets the draft, so if there are concurrent calls to e.g. draft_set_text and draft_set_subject, only one will be applied.

Maybe better manage the draft in the UI and call only create_draft and some sort of "send message" API that sends the draft passed from the UI as a whole and resets the draft?

The idea of managing the draft on the core side and updating it does not look like a good API to me, it's just how it is historically.

If we at some point make it possible to connect to remote core, managing the draft over high-latency JSON-RPC connection is going to be even worse.

.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<u32>,
) -> 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<QuotedText>,
) -> 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<u32> {
// 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<u32> {
let ctx = self.get_context(account_id).await?;
chat::send_videochat_invitation(&ctx, ChatId::new(chat_id))
Expand Down Expand Up @@ -2195,6 +2321,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,
Expand Down Expand Up @@ -2236,6 +2363,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<u32> {
let ctx = self.get_context(account_id).await?;
if let Some(draft) = ChatId::new(chat_id).get_draft(&ctx).await? {
Expand Down
10 changes: 10 additions & 0 deletions deltachat-jsonrpc/src/api/types/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -708,3 +708,13 @@ impl From<deltachat::ephemeral::Timer> 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,
}
Loading