mirror of
https://github.com/chatmail/core.git
synced 2026-04-19 06:26:30 +03:00
jsonrpc: add more functions, mostly message related (#3590)
* add more functions, see changelog for details * add pr number to changelog * clarify doc comment * clarify usage of BasicChat and adjust properties acordingly r10s is right it should only contain what we need of the expensive calls * fix doc typos * run cargo fmt * jsonrpc: add connectivity functions * fix typo * fix typo * Add get_contact_encryption_info and get_connectivity_html Fix get_connectivity_html and get_encrinfo futures not being Send. See https://github.com/rust-lang/rust/issues/101650 for more information. Co-authored-by: jikstra <jikstra@disroot.org> * Update CHANGELOG * Update typescript files * remove todo from changelog Co-authored-by: jikstra <jikstra@disroot.org>
This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use deltachat::{
|
||||
chat::{add_contact_to_chat, get_chat_media, get_chat_msgs, remove_contact_from_chat, ChatId},
|
||||
chat::{
|
||||
self, add_contact_to_chat, forward_msgs, get_chat_media, get_chat_msgs, marknoticed_chat,
|
||||
remove_contact_from_chat, Chat, ChatId, ChatItem,
|
||||
},
|
||||
chatlist::Chatlist,
|
||||
config::Config,
|
||||
contact::{may_be_valid_addr, Contact, ContactId},
|
||||
context::get_info,
|
||||
message::{delete_msgs, get_msg_info, Message, MsgId, Viewtype},
|
||||
message::{delete_msgs, get_msg_info, markseen_msgs, Message, MessageState, MsgId, Viewtype},
|
||||
provider::get_provider_info,
|
||||
qr,
|
||||
qr_code_generator::get_securejoin_qr_svg,
|
||||
@@ -34,7 +37,10 @@ use types::message::MessageObject;
|
||||
use types::provider_info::ProviderInfo;
|
||||
use types::webxdc::WebxdcMessageInfo;
|
||||
|
||||
use self::types::message::MessageViewtype;
|
||||
use self::types::{
|
||||
chat::{BasicChat, MuteDuration},
|
||||
message::MessageViewtype,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CommandApi {
|
||||
@@ -368,6 +374,13 @@ impl CommandApi {
|
||||
FullChat::try_from_dc_chat_id(&ctx, chat_id).await
|
||||
}
|
||||
|
||||
/// get basic info about a chat,
|
||||
/// use chatlist_get_full_chat_by_id() instead if you need more information
|
||||
async fn get_basic_chat_info(&self, account_id: u32, chat_id: u32) -> Result<BasicChat> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
BasicChat::try_from_dc_chat_id(&ctx, chat_id).await
|
||||
}
|
||||
|
||||
async fn accept_chat(&self, account_id: u32, chat_id: u32) -> Result<()> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
ChatId::new(chat_id).accept(&ctx).await
|
||||
@@ -427,6 +440,8 @@ impl CommandApi {
|
||||
/// If not set, the Setup-Contact protocol is offered in the QR code.
|
||||
/// See https://countermitm.readthedocs.io/en/latest/new.html
|
||||
/// for details about both protocols.
|
||||
///
|
||||
/// return format: `[code, svg]`
|
||||
// TODO fix doc comment after adding dc_join_securejoin
|
||||
async fn get_chat_securejoin_qr_code_svg(
|
||||
&self,
|
||||
@@ -495,10 +510,101 @@ impl CommandApi {
|
||||
Ok(message_id.to_u32())
|
||||
}
|
||||
|
||||
/// Mark all messages in a chat as _noticed_.
|
||||
/// _Noticed_ messages are no longer _fresh_ and do not count as being unseen
|
||||
/// but are still waiting for being marked as "seen" using markseen_msgs()
|
||||
/// (IMAP/MDNs is not done for noticed messages).
|
||||
///
|
||||
/// Calling this function usually results in the event #DC_EVENT_MSGS_NOTICED.
|
||||
/// See also markseen_msgs().
|
||||
async fn marknoticed_chat(&self, account_id: u32, chat_id: u32) -> Result<()> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
marknoticed_chat(&ctx, ChatId::new(chat_id)).await
|
||||
}
|
||||
|
||||
async fn get_first_unread_message_of_chat(
|
||||
&self,
|
||||
account_id: u32,
|
||||
chat_id: u32,
|
||||
) -> Result<Option<u32>> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
|
||||
// TODO: implement this in core with an SQL query, that will be way faster
|
||||
let messages = get_chat_msgs(&ctx, ChatId::new(chat_id), 0).await?;
|
||||
let mut first_unread_message_id = None;
|
||||
for item in messages.into_iter().rev() {
|
||||
if let ChatItem::Message { msg_id } = item {
|
||||
match msg_id.get_state(&ctx).await? {
|
||||
MessageState::InSeen => break,
|
||||
MessageState::InFresh | MessageState::InNoticed => {
|
||||
first_unread_message_id = Some(msg_id)
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(first_unread_message_id.map(|id| id.to_u32()))
|
||||
}
|
||||
|
||||
/// Set mute duration of a chat.
|
||||
///
|
||||
/// The UI can then call is_chat_muted() when receiving a new message
|
||||
/// to decide whether it should trigger an notification.
|
||||
///
|
||||
/// Muted chats should not sound or vibrate
|
||||
/// and should not show a visual notification in the system area.
|
||||
/// Moreover, muted chats should be excluded from global badge counter
|
||||
/// (get_fresh_msgs() skips muted chats therefore)
|
||||
/// and the in-app, per-chat badge counter should use a less obtrusive color.
|
||||
///
|
||||
/// Sends out #DC_EVENT_CHAT_MODIFIED.
|
||||
async fn set_chat_mute_duration(
|
||||
&self,
|
||||
account_id: u32,
|
||||
chat_id: u32,
|
||||
duration: MuteDuration,
|
||||
) -> Result<()> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
chat::set_muted(&ctx, ChatId::new(chat_id), duration.try_into_core_type()?).await
|
||||
}
|
||||
|
||||
/// Check whether the chat is currently muted (can be changed by set_chat_mute_duration()).
|
||||
///
|
||||
/// This is available as a standalone function outside of fullchat, because it might be only needed for notification
|
||||
async fn is_chat_muted(&self, account_id: u32, chat_id: u32) -> Result<bool> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
Ok(Chat::load_from_db(&ctx, ChatId::new(chat_id))
|
||||
.await?
|
||||
.is_muted())
|
||||
}
|
||||
|
||||
// ---------------------------------------------
|
||||
// message list
|
||||
// ---------------------------------------------
|
||||
|
||||
/// Mark messages as presented to the user.
|
||||
/// Typically, UIs call this function on scrolling through the message list,
|
||||
/// when the messages are presented at least for a little moment.
|
||||
/// The concrete action depends on the type of the chat and on the users settings
|
||||
/// (dc_msgs_presented() may be a better name therefore, but well. :)
|
||||
///
|
||||
/// - For normal chats, the IMAP state is updated, MDN is sent
|
||||
/// (if set_config()-options `mdns_enabled` is set)
|
||||
/// and the internal state is changed to @ref DC_STATE_IN_SEEN to reflect these actions.
|
||||
///
|
||||
/// - For contact requests, no IMAP or MDNs is done
|
||||
/// and the internal state is not changed therefore.
|
||||
/// See also marknoticed_chat().
|
||||
///
|
||||
/// Moreover, timer is started for incoming ephemeral messages.
|
||||
/// This also happens for contact requests chats.
|
||||
///
|
||||
/// One #DC_EVENT_MSGS_NOTICED event is emitted per modified chat.
|
||||
async fn markseen_msgs(&self, account_id: u32, msg_ids: Vec<u32>) -> Result<()> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
markseen_msgs(&ctx, msg_ids.into_iter().map(MsgId::new).collect()).await
|
||||
}
|
||||
|
||||
async fn message_list_get_message_ids(
|
||||
&self,
|
||||
account_id: u32,
|
||||
@@ -687,6 +793,19 @@ impl CommandApi {
|
||||
}
|
||||
Ok(contacts)
|
||||
}
|
||||
|
||||
/// Get encryption info for a contact.
|
||||
/// Get a multi-line encryption info, containing your fingerprint and the
|
||||
/// fingerprint of the contact, used e.g. to compare the fingerprints for a simple out-of-band verification.
|
||||
async fn get_contact_encryption_info(
|
||||
&self,
|
||||
account_id: u32,
|
||||
contact_id: u32,
|
||||
) -> Result<String> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
Contact::get_encrinfo(&ctx, ContactId::new(contact_id)).await
|
||||
}
|
||||
|
||||
// ---------------------------------------------
|
||||
// chat
|
||||
// ---------------------------------------------
|
||||
@@ -722,6 +841,50 @@ impl CommandApi {
|
||||
Ok(media.iter().map(|msg_id| msg_id.to_u32()).collect())
|
||||
}
|
||||
|
||||
// ---------------------------------------------
|
||||
// connectivity
|
||||
// ---------------------------------------------
|
||||
|
||||
/// Indicate that the network likely has come back.
|
||||
/// or just that the network conditions might have changed
|
||||
async fn maybe_network(&self) -> Result<()> {
|
||||
self.accounts.read().await.maybe_network().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the current connectivity, i.e. whether the device is connected to the IMAP server.
|
||||
/// One of:
|
||||
/// - DC_CONNECTIVITY_NOT_CONNECTED (1000-1999): Show e.g. the string "Not connected" or a red dot
|
||||
/// - DC_CONNECTIVITY_CONNECTING (2000-2999): Show e.g. the string "Connecting…" or a yellow dot
|
||||
/// - DC_CONNECTIVITY_WORKING (3000-3999): Show e.g. the string "Getting new messages" or a spinning wheel
|
||||
/// - DC_CONNECTIVITY_CONNECTED (>=4000): Show e.g. the string "Connected" or a green dot
|
||||
///
|
||||
/// We don't use exact values but ranges here so that we can split up
|
||||
/// states into multiple states in the future.
|
||||
///
|
||||
/// Meant as a rough overview that can be shown
|
||||
/// e.g. in the title of the main screen.
|
||||
///
|
||||
/// If the connectivity changes, a #DC_EVENT_CONNECTIVITY_CHANGED will be emitted.
|
||||
async fn get_connectivity(&self, account_id: u32) -> Result<u32> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
Ok(ctx.get_connectivity().await as u32)
|
||||
}
|
||||
|
||||
/// Get an overview of the current connectivity, and possibly more statistics.
|
||||
/// Meant to give the user more insight about the current status than
|
||||
/// the basic connectivity info returned by get_connectivity(); show this
|
||||
/// e.g., if the user taps on said basic connectivity info.
|
||||
///
|
||||
/// If this page changes, a #DC_EVENT_CONNECTIVITY_CHANGED will be emitted.
|
||||
///
|
||||
/// This comes as an HTML from the core so that we can easily improve it
|
||||
/// and the improvement instantly reaches all UIs.
|
||||
async fn get_connectivity_html(&self, account_id: u32) -> Result<String> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
ctx.get_connectivity_html().await
|
||||
}
|
||||
|
||||
// ---------------------------------------------
|
||||
// webxdc
|
||||
// ---------------------------------------------
|
||||
@@ -762,6 +925,45 @@ impl CommandApi {
|
||||
WebxdcMessageInfo::get_for_message(&ctx, MsgId::new(instance_msg_id)).await
|
||||
}
|
||||
|
||||
/// Forward messages to another chat.
|
||||
///
|
||||
/// All types of messages can be forwarded,
|
||||
/// however, they will be flagged as such (dc_msg_is_forwarded() is set).
|
||||
///
|
||||
/// Original sender, info-state and webxdc updates are not forwarded on purpose.
|
||||
async fn forward_messages(
|
||||
&self,
|
||||
account_id: u32,
|
||||
message_ids: Vec<u32>,
|
||||
chat_id: u32,
|
||||
) -> Result<()> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
let message_ids: Vec<MsgId> = message_ids.into_iter().map(MsgId::new).collect();
|
||||
forward_msgs(&ctx, &message_ids, ChatId::new(chat_id)).await
|
||||
}
|
||||
|
||||
// ---------------------------------------------
|
||||
// functions for the composer
|
||||
// the composer is the message input field
|
||||
// ---------------------------------------------
|
||||
|
||||
async fn remove_draft(&self, account_id: u32, chat_id: u32) -> Result<()> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
ChatId::new(chat_id).set_draft(&ctx, None).await
|
||||
}
|
||||
|
||||
/// Get draft for a chat, if any.
|
||||
async fn get_draft(&self, account_id: u32, chat_id: u32) -> Result<Option<MessageObject>> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
if let Some(draft) = ChatId::new(chat_id).get_draft(&ctx).await? {
|
||||
Ok(Some(
|
||||
MessageObject::from_msg_id(&ctx, draft.get_id()).await?,
|
||||
))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------
|
||||
// misc prototyping functions
|
||||
// that might get removed later again
|
||||
@@ -782,6 +984,91 @@ impl CommandApi {
|
||||
let message_id = deltachat::chat::send_msg(&ctx, ChatId::new(chat_id), &mut msg).await?;
|
||||
Ok(message_id.to_u32())
|
||||
}
|
||||
|
||||
// mimics the old desktop call, will get replaced with something better in the composer rewrite,
|
||||
// the better version will just be sending the current draft, though there will be probably something similar with more options to this for the corner cases like setting a marker on the map
|
||||
async fn misc_send_msg(
|
||||
&self,
|
||||
account_id: u32,
|
||||
chat_id: u32,
|
||||
text: Option<String>,
|
||||
file: Option<String>,
|
||||
location: Option<(f64, f64)>,
|
||||
quoted_message_id: Option<u32>,
|
||||
) -> Result<(u32, MessageObject)> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
let mut message = Message::new(if file.is_some() {
|
||||
Viewtype::File
|
||||
} else {
|
||||
Viewtype::Text
|
||||
});
|
||||
if text.is_some() {
|
||||
message.set_text(text);
|
||||
}
|
||||
if let Some(file) = file {
|
||||
message.set_file(file, None);
|
||||
}
|
||||
if let Some((latitude, longitude)) = location {
|
||||
message.set_location(latitude, longitude);
|
||||
}
|
||||
if let Some(id) = quoted_message_id {
|
||||
message
|
||||
.set_quote(
|
||||
&ctx,
|
||||
Some(
|
||||
&Message::load_from_db(&ctx, MsgId::new(id))
|
||||
.await
|
||||
.context("message to quote could not be loaded")?,
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
let msg_id = chat::send_msg(&ctx, ChatId::new(chat_id), &mut message)
|
||||
.await?
|
||||
.to_u32();
|
||||
let message = MessageObject::from_message_id(&ctx, msg_id).await?;
|
||||
Ok((msg_id, message))
|
||||
}
|
||||
|
||||
// mimics the old desktop call, will get replaced with something better in the composer rewrite,
|
||||
// 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
|
||||
async fn misc_set_draft(
|
||||
&self,
|
||||
account_id: u32,
|
||||
chat_id: u32,
|
||||
text: Option<String>,
|
||||
file: Option<String>,
|
||||
quoted_message_id: Option<u32>,
|
||||
) -> Result<()> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
let mut draft = Message::new(if file.is_some() {
|
||||
Viewtype::File
|
||||
} else {
|
||||
Viewtype::Text
|
||||
});
|
||||
if text.is_some() {
|
||||
draft.set_text(text);
|
||||
}
|
||||
if let Some(file) = file {
|
||||
draft.set_file(file, None);
|
||||
}
|
||||
if let Some(id) = quoted_message_id {
|
||||
draft
|
||||
.set_quote(
|
||||
&ctx,
|
||||
Some(
|
||||
&Message::load_from_db(&ctx, MsgId::new(id))
|
||||
.await
|
||||
.context("message to quote could not be loaded")?,
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
ChatId::new(chat_id).set_draft(&ctx, Some(&mut draft)).await
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions (to prevent code duplication)
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use deltachat::chat::get_chat_contacts;
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use deltachat::chat::{self, get_chat_contacts};
|
||||
use deltachat::chat::{Chat, ChatId};
|
||||
use deltachat::constants::Chattype;
|
||||
use deltachat::contact::{Contact, ContactId};
|
||||
use deltachat::context::Context;
|
||||
use num_traits::cast::ToPrimitive;
|
||||
use serde::Serialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use typescript_type_def::TypeDef;
|
||||
|
||||
use super::color_int_to_hex_string;
|
||||
@@ -83,7 +85,7 @@ impl FullChat {
|
||||
name: chat.name.clone(),
|
||||
is_protected: chat.is_protected(),
|
||||
profile_image, //BLOBS ?
|
||||
archived: chat.get_visibility() == deltachat::chat::ChatVisibility::Archived,
|
||||
archived: chat.get_visibility() == chat::ChatVisibility::Archived,
|
||||
chat_type: chat
|
||||
.get_type()
|
||||
.to_u32()
|
||||
@@ -104,3 +106,86 @@ impl FullChat {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// cheaper version of fullchat, omits:
|
||||
/// - contacts
|
||||
/// - contact_ids
|
||||
/// - fresh_message_counter
|
||||
/// - ephemeral_timer
|
||||
/// - self_in_group
|
||||
/// - was_seen_recently
|
||||
/// - can_send
|
||||
///
|
||||
/// used when you only need the basic metadata of a chat like type, name, profile picture
|
||||
#[derive(Serialize, TypeDef)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BasicChat {
|
||||
id: u32,
|
||||
name: String,
|
||||
is_protected: bool,
|
||||
profile_image: Option<String>, //BLOBS ?
|
||||
archived: bool,
|
||||
chat_type: u32,
|
||||
is_unpromoted: bool,
|
||||
is_self_talk: bool,
|
||||
color: String,
|
||||
is_contact_request: bool,
|
||||
is_device_chat: bool,
|
||||
is_muted: bool,
|
||||
}
|
||||
|
||||
impl BasicChat {
|
||||
pub async fn try_from_dc_chat_id(context: &Context, chat_id: u32) -> Result<Self> {
|
||||
let rust_chat_id = ChatId::new(chat_id);
|
||||
let chat = Chat::load_from_db(context, rust_chat_id).await?;
|
||||
|
||||
let profile_image = match chat.get_profile_image(context).await? {
|
||||
Some(path_buf) => path_buf.to_str().map(|s| s.to_owned()),
|
||||
None => None,
|
||||
};
|
||||
let color = color_int_to_hex_string(chat.get_color(context).await?);
|
||||
|
||||
Ok(BasicChat {
|
||||
id: chat_id,
|
||||
name: chat.name.clone(),
|
||||
is_protected: chat.is_protected(),
|
||||
profile_image, //BLOBS ?
|
||||
archived: chat.get_visibility() == chat::ChatVisibility::Archived,
|
||||
chat_type: chat
|
||||
.get_type()
|
||||
.to_u32()
|
||||
.ok_or_else(|| anyhow!("unknown chat type id"))?, // TODO get rid of this unwrap?
|
||||
is_unpromoted: chat.is_unpromoted(),
|
||||
is_self_talk: chat.is_self_talk(),
|
||||
color,
|
||||
is_contact_request: chat.is_contact_request(),
|
||||
is_device_chat: chat.is_device_talk(),
|
||||
is_muted: chat.is_muted(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, TypeDef)]
|
||||
pub enum MuteDuration {
|
||||
NotMuted,
|
||||
Forever,
|
||||
Until(i64),
|
||||
}
|
||||
|
||||
impl MuteDuration {
|
||||
pub fn try_into_core_type(self) -> Result<chat::MuteDuration> {
|
||||
match self {
|
||||
MuteDuration::NotMuted => Ok(chat::MuteDuration::NotMuted),
|
||||
MuteDuration::Forever => Ok(chat::MuteDuration::Forever),
|
||||
MuteDuration::Until(n) => {
|
||||
if n <= 0 {
|
||||
bail!("failed to read mute duration")
|
||||
}
|
||||
|
||||
Ok(SystemTime::now()
|
||||
.checked_add(Duration::from_secs(n as u64))
|
||||
.map_or(chat::MuteDuration::Forever, chat::MuteDuration::Until))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ pub struct ContactObject {
|
||||
name_and_addr: String,
|
||||
is_blocked: bool,
|
||||
is_verified: bool,
|
||||
/// the contact's last seen timestamp
|
||||
last_seen: i64,
|
||||
was_seen_recently: bool,
|
||||
}
|
||||
|
||||
@@ -46,6 +48,7 @@ impl ContactObject {
|
||||
name_and_addr: contact.get_name_n_addr(),
|
||||
is_blocked: contact.is_blocked(),
|
||||
is_verified,
|
||||
last_seen: contact.last_seen(),
|
||||
was_seen_recently: contact.was_seen_recently(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use deltachat::contact::Contact;
|
||||
use deltachat::context::Context;
|
||||
use deltachat::download;
|
||||
use deltachat::message::Message;
|
||||
use deltachat::message::MsgId;
|
||||
use deltachat::message::Viewtype;
|
||||
@@ -9,6 +10,7 @@ use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use typescript_type_def::TypeDef;
|
||||
|
||||
use super::color_int_to_hex_string;
|
||||
use super::contact::ContactObject;
|
||||
use super::webxdc::WebxdcMessageInfo;
|
||||
|
||||
@@ -18,8 +20,9 @@ pub struct MessageObject {
|
||||
id: u32,
|
||||
chat_id: u32,
|
||||
from_id: u32,
|
||||
quoted_text: Option<String>,
|
||||
quoted_message_id: Option<u32>,
|
||||
quote: Option<MessageQuote>,
|
||||
parent_id: Option<u32>,
|
||||
|
||||
text: Option<String>,
|
||||
has_location: bool,
|
||||
has_html: bool,
|
||||
@@ -56,17 +59,36 @@ pub struct MessageObject {
|
||||
file_name: Option<String>,
|
||||
|
||||
webxdc_info: Option<WebxdcMessageInfo>,
|
||||
|
||||
download_state: DownloadState,
|
||||
}
|
||||
|
||||
#[derive(Serialize, TypeDef)]
|
||||
#[serde(tag = "kind")]
|
||||
enum MessageQuote {
|
||||
JustText {
|
||||
text: String,
|
||||
},
|
||||
#[serde(rename_all = "camelCase")]
|
||||
WithMessage {
|
||||
text: String,
|
||||
message_id: u32,
|
||||
author_display_name: String,
|
||||
author_display_color: String,
|
||||
override_sender_name: Option<String>,
|
||||
image: Option<String>,
|
||||
is_forwarded: bool,
|
||||
},
|
||||
}
|
||||
|
||||
impl MessageObject {
|
||||
pub async fn from_message_id(context: &Context, message_id: u32) -> Result<Self> {
|
||||
let msg_id = MsgId::new(message_id);
|
||||
let message = Message::load_from_db(context, msg_id).await?;
|
||||
Self::from_msg_id(context, msg_id).await
|
||||
}
|
||||
|
||||
let quoted_message_id = message
|
||||
.quoted_message(context)
|
||||
.await?
|
||||
.map(|m| m.get_id().to_u32());
|
||||
pub async fn from_msg_id(context: &Context, msg_id: MsgId) -> Result<Self> {
|
||||
let message = Message::load_from_db(context, msg_id).await?;
|
||||
|
||||
let sender_contact = Contact::load_from_db(context, message.get_from_id()).await?;
|
||||
let sender = ContactObject::try_from_dc_contact(context, sender_contact).await?;
|
||||
@@ -79,12 +101,45 @@ impl MessageObject {
|
||||
None
|
||||
};
|
||||
|
||||
let parent_id = message.parent(context).await?.map(|m| m.get_id().to_u32());
|
||||
|
||||
let download_state = message.download_state().into();
|
||||
|
||||
let quote = if let Some(quoted_text) = message.quoted_text() {
|
||||
match message.quoted_message(context).await? {
|
||||
Some(quote) => {
|
||||
let quote_author = Contact::load_from_db(context, quote.get_from_id()).await?;
|
||||
Some(MessageQuote::WithMessage {
|
||||
text: quoted_text,
|
||||
message_id: quote.get_id().to_u32(),
|
||||
author_display_name: quote_author.get_display_name().to_owned(),
|
||||
author_display_color: color_int_to_hex_string(quote_author.get_color()),
|
||||
override_sender_name: quote.get_override_sender_name(),
|
||||
image: if quote.get_viewtype() == Viewtype::Image
|
||||
|| quote.get_viewtype() == Viewtype::Gif
|
||||
{
|
||||
match quote.get_file(context) {
|
||||
Some(path_buf) => path_buf.to_str().map(|s| s.to_owned()),
|
||||
None => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
},
|
||||
is_forwarded: quote.is_forwarded(),
|
||||
})
|
||||
}
|
||||
None => Some(MessageQuote::JustText { text: quoted_text }),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(MessageObject {
|
||||
id: message_id,
|
||||
id: msg_id.to_u32(),
|
||||
chat_id: message.get_chat_id().to_u32(),
|
||||
from_id: message.get_from_id().to_u32(),
|
||||
quoted_text: message.quoted_text(),
|
||||
quoted_message_id,
|
||||
quote,
|
||||
parent_id,
|
||||
text: message.get_text(),
|
||||
has_location: message.has_location(),
|
||||
has_html: message.has_html(),
|
||||
@@ -131,6 +186,8 @@ impl MessageObject {
|
||||
file_bytes,
|
||||
file_name: message.get_filename(),
|
||||
webxdc_info,
|
||||
|
||||
download_state,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -210,3 +267,22 @@ impl From<MessageViewtype> for Viewtype {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, TypeDef)]
|
||||
pub enum DownloadState {
|
||||
Done,
|
||||
Available,
|
||||
Failure,
|
||||
InProgress,
|
||||
}
|
||||
|
||||
impl From<download::DownloadState> for DownloadState {
|
||||
fn from(state: download::DownloadState) -> Self {
|
||||
match state {
|
||||
download::DownloadState::Done => DownloadState::Done,
|
||||
download::DownloadState::Available => DownloadState::Available,
|
||||
download::DownloadState::Failure => DownloadState::Failure,
|
||||
download::DownloadState::InProgress => DownloadState::InProgress,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user