Strongly type stock strings

This changes the internal stock strings API to be more strongly typed,
ensuring that the caller can not construct the stock string in the
wrong way.

The old approach left it to the callers to figure out how a stock
string should be created, now each stock string has their specific
arguments and callers can not make mistakes.  In particular all the
subtleties and different ways of calling stock_system_msg() disappear.

This could not use a trait for stock strings, as this would not allow
having per-message typed arguments.  So we needed a type per message
with a custom method, only by convention this method is .stock_str().
The type is a enum without variants to avoid allowing someone to
create the type.

Sadly the fallback string and substitutions are still far away from
each other, but it is now only one place which needs to know how to
construct the string instead of many.
This commit is contained in:
Floris Bruynooghe
2021-02-07 15:52:30 +01:00
parent ad640e163c
commit 29f184b4c4
19 changed files with 1488 additions and 647 deletions

View File

@@ -1,6 +1,6 @@
//! # Chat module //! # Chat module
use deltachat_derive::{FromSql, ToSql}; use std::borrow::Cow;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::str::FromStr; use std::str::FromStr;
use std::time::{Duration, SystemTime}; use std::time::{Duration, SystemTime};
@@ -8,6 +8,7 @@ use std::time::{Duration, SystemTime};
use anyhow::Context as _; use anyhow::Context as _;
use anyhow::{bail, ensure, format_err, Error}; use anyhow::{bail, ensure, format_err, Error};
use async_std::path::{Path, PathBuf}; use async_std::path::{Path, PathBuf};
use deltachat_derive::{FromSql, ToSql};
use itertools::Itertools; use itertools::Itertools;
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -38,7 +39,11 @@ use crate::mimeparser::SystemMessage;
use crate::param::{Param, Params}; use crate::param::{Param, Params};
use crate::peerstate::{Peerstate, PeerstateVerifiedStatus}; use crate::peerstate::{Peerstate, PeerstateVerifiedStatus};
use crate::sql; use crate::sql;
use crate::stock::StockMessage; use crate::stock::{
ArchivedChats, DeadDrop, DeviceMessages, E2eAvailable, E2ePreferred, EncrNone, MsgAddMember,
MsgDelMember, MsgGroupLeft, MsgGrpImgChanged, MsgGrpImgDeleted, MsgGrpName, NewGroupDraft,
SavedMessages, SelfDeletedMsgBody, VideochatInviteMsgBody,
};
/// An chat item, such as a message or a marker. /// An chat item, such as a message or a marker.
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
@@ -396,12 +401,7 @@ impl ChatId {
if chat.is_self_talk() { if chat.is_self_talk() {
let mut msg = Message::new(Viewtype::Text); let mut msg = Message::new(Viewtype::Text);
msg.text = Some( msg.text = Some(SelfDeletedMsgBody::stock_str(context).await.into());
context
.stock_str(StockMessage::SelfDeletedMsgBody)
.await
.into(),
);
add_device_msg(&context, None, Some(&mut msg)).await?; add_device_msg(&context, None, Some(&mut msg)).await?;
} }
@@ -659,23 +659,23 @@ impl ChatId {
let addr = contact.get_addr(); let addr = contact.get_addr();
let peerstate = Peerstate::from_addr(context, addr).await?; let peerstate = Peerstate::from_addr(context, addr).await?;
let stock_message = peerstate let stock_message = match peerstate
.filter(|peerstate| { .filter(|peerstate| {
peerstate peerstate
.peek_key(PeerstateVerifiedStatus::Unverified) .peek_key(PeerstateVerifiedStatus::Unverified)
.is_some() .is_some()
}) })
.map(|peerstate| match peerstate.prefer_encrypt { .map(|peerstate| peerstate.prefer_encrypt)
EncryptPreference::Mutual => StockMessage::E2ePreferred, {
EncryptPreference::NoPreference => StockMessage::E2eAvailable, Some(EncryptPreference::Mutual) => E2ePreferred::stock_str(context).await,
EncryptPreference::Reset => StockMessage::EncrNone, Some(EncryptPreference::NoPreference) => E2eAvailable::stock_str(context).await,
}) Some(EncryptPreference::Reset) => EncrNone::stock_str(context).await,
.unwrap_or(StockMessage::EncrNone); None => EncrNone::stock_str(context).await,
};
if !ret.is_empty() { if !ret.is_empty() {
ret.push('\n') ret.push('\n')
} }
ret += &format!("{} {}", addr, context.stock_str(stock_message).await); ret += &format!("{} {}", addr, stock_message);
} }
Ok(ret) Ok(ret)
@@ -793,9 +793,9 @@ impl Chat {
} }
Ok(mut chat) => { Ok(mut chat) => {
if chat.id.is_deaddrop() { if chat.id.is_deaddrop() {
chat.name = context.stock_str(StockMessage::DeadDrop).await.into(); chat.name = DeadDrop::stock_str(context).await.into();
} else if chat.id.is_archived_link() { } else if chat.id.is_archived_link() {
let tempname = context.stock_str(StockMessage::ArchivedChats).await; let tempname = ArchivedChats::stock_str(context).await;
let cnt = dc_get_archived_cnt(context).await; let cnt = dc_get_archived_cnt(context).await;
chat.name = format!("{} ({})", tempname, cnt); chat.name = format!("{} ({})", tempname, cnt);
} else { } else {
@@ -810,9 +810,9 @@ impl Chat {
chat.name = chat_name; chat.name = chat_name;
} }
if chat.param.exists(Param::Selftalk) { if chat.param.exists(Param::Selftalk) {
chat.name = context.stock_str(StockMessage::SavedMessages).await.into(); chat.name = SavedMessages::stock_str(context).await.into();
} else if chat.param.exists(Param::Devicetalk) { } else if chat.param.exists(Param::Devicetalk) {
chat.name = context.stock_str(StockMessage::DeviceMessages).await.into(); chat.name = DeviceMessages::stock_str(context).await.into();
} }
} }
Ok(chat) Ok(chat)
@@ -1411,10 +1411,10 @@ pub(crate) async fn update_device_icon(context: &Context) -> Result<(), Error> {
async fn update_special_chat_name( async fn update_special_chat_name(
context: &Context, context: &Context,
contact_id: u32, contact_id: u32,
stock_id: StockMessage, name: Cow<'static, str>,
) -> Result<(), Error> { ) -> Result<(), Error> {
if let Ok((chat_id, _)) = lookup_by_contact_id(context, contact_id).await { if let Ok((chat_id, _)) = lookup_by_contact_id(context, contact_id).await {
let name: String = context.stock_str(stock_id).await.into(); let name: String = name.into();
// the `!= name` condition avoids unneeded writes // the `!= name` condition avoids unneeded writes
context context
.sql .sql
@@ -1428,8 +1428,18 @@ async fn update_special_chat_name(
} }
pub(crate) async fn update_special_chat_names(context: &Context) -> Result<(), Error> { pub(crate) async fn update_special_chat_names(context: &Context) -> Result<(), Error> {
update_special_chat_name(context, DC_CONTACT_ID_DEVICE, StockMessage::DeviceMessages).await?; update_special_chat_name(
update_special_chat_name(context, DC_CONTACT_ID_SELF, StockMessage::SavedMessages).await?; context,
DC_CONTACT_ID_DEVICE,
DeviceMessages::stock_str(context).await,
)
.await?;
update_special_chat_name(
context,
DC_CONTACT_ID_SELF,
SavedMessages::stock_str(context).await,
)
.await?;
Ok(()) Ok(())
} }
@@ -1812,12 +1822,9 @@ pub async fn send_videochat_invitation(context: &Context, chat_id: ChatId) -> Re
let mut msg = Message::new(Viewtype::VideochatInvitation); let mut msg = Message::new(Viewtype::VideochatInvitation);
msg.param.set(Param::WebrtcRoom, &instance); msg.param.set(Param::WebrtcRoom, &instance);
msg.text = Some( msg.text = Some(
context VideochatInviteMsgBody::stock_str(context, Message::parse_webrtc_instance(&instance).1)
.stock_string_repl_str( .await
StockMessage::VideochatInviteMsgBody, .to_string(),
Message::parse_webrtc_instance(&instance).1,
)
.await,
); );
send_msg(context, chat_id, &mut msg).await send_msg(context, chat_id, &mut msg).await
} }
@@ -2154,9 +2161,9 @@ pub async fn create_group_chat(
let chat_name = improve_single_line_input(chat_name); let chat_name = improve_single_line_input(chat_name);
ensure!(!chat_name.is_empty(), "Invalid chat name"); ensure!(!chat_name.is_empty(), "Invalid chat name");
let draft_txt = context let draft_txt = NewGroupDraft::stock_str(context, &chat_name)
.stock_string_repl_str(StockMessage::NewGroupDraft, &chat_name) .await
.await; .to_string();
let grpid = dc_create_id(); let grpid = dc_create_id();
context.sql.execute( context.sql.execute(
@@ -2334,14 +2341,9 @@ pub(crate) async fn add_contact_to_chat_ex(
if chat.param.get_int(Param::Unpromoted).unwrap_or_default() == 0 { if chat.param.get_int(Param::Unpromoted).unwrap_or_default() == 0 {
msg.viewtype = Viewtype::Text; msg.viewtype = Viewtype::Text;
msg.text = Some( msg.text = Some(
context MsgAddMember::stock_str(context, contact.get_addr(), DC_CONTACT_ID_SELF)
.stock_system_msg( .await
StockMessage::MsgAddMember, .to_string(),
contact.get_addr(),
"",
DC_CONTACT_ID_SELF as u32,
)
.await,
); );
msg.param.set_cmd(SystemMessage::MemberAddedToGroup); msg.param.set_cmd(SystemMessage::MemberAddedToGroup);
msg.param.set(Param::Arg, contact.get_addr()); msg.param.set(Param::Arg, contact.get_addr());
@@ -2537,25 +2539,19 @@ pub async fn remove_contact_from_chat(
if contact.id == DC_CONTACT_ID_SELF { if contact.id == DC_CONTACT_ID_SELF {
set_group_explicitly_left(context, chat.grpid).await?; set_group_explicitly_left(context, chat.grpid).await?;
msg.text = Some( msg.text = Some(
context MsgGroupLeft::stock_str(context, DC_CONTACT_ID_SELF)
.stock_system_msg( .await
StockMessage::MsgGroupLeft, .to_string(),
"",
"",
DC_CONTACT_ID_SELF,
)
.await,
); );
} else { } else {
msg.text = Some( msg.text = Some(
context MsgDelMember::stock_str(
.stock_system_msg( context,
StockMessage::MsgDelMember, contact.get_addr(),
contact.get_addr(), DC_CONTACT_ID_SELF,
"", )
DC_CONTACT_ID_SELF, .await
) .to_string(),
.await,
); );
} }
msg.param.set_cmd(SystemMessage::MemberRemovedFromGroup); msg.param.set_cmd(SystemMessage::MemberRemovedFromGroup);
@@ -2651,14 +2647,9 @@ pub async fn set_chat_name(
if chat.is_promoted() && !chat.is_mailing_list() { if chat.is_promoted() && !chat.is_mailing_list() {
msg.viewtype = Viewtype::Text; msg.viewtype = Viewtype::Text;
msg.text = Some( msg.text = Some(
context MsgGrpName::stock_str(context, &chat.name, &new_name, DC_CONTACT_ID_SELF)
.stock_system_msg( .await
StockMessage::MsgGrpName, .to_string(),
&chat.name,
&new_name,
DC_CONTACT_ID_SELF,
)
.await,
); );
msg.param.set_cmd(SystemMessage::GroupNameChanged); msg.param.set_cmd(SystemMessage::GroupNameChanged);
if !chat.name.is_empty() { if !chat.name.is_empty() {
@@ -2716,9 +2707,9 @@ pub async fn set_chat_profile_image(
chat.param.remove(Param::ProfileImage); chat.param.remove(Param::ProfileImage);
msg.param.remove(Param::Arg); msg.param.remove(Param::Arg);
msg.text = Some( msg.text = Some(
context MsgGrpImgDeleted::stock_str(context, DC_CONTACT_ID_SELF)
.stock_system_msg(StockMessage::MsgGrpImgDeleted, "", "", DC_CONTACT_ID_SELF) .await
.await, .to_string(),
); );
} else { } else {
let image_blob = match BlobObject::from_path(context, Path::new(new_image.as_ref())) { let image_blob = match BlobObject::from_path(context, Path::new(new_image.as_ref())) {
@@ -2734,9 +2725,9 @@ pub async fn set_chat_profile_image(
chat.param.set(Param::ProfileImage, image_blob.as_name()); chat.param.set(Param::ProfileImage, image_blob.as_name());
msg.param.set(Param::Arg, image_blob.as_name()); msg.param.set(Param::Arg, image_blob.as_name());
msg.text = Some( msg.text = Some(
context MsgGrpImgChanged::stock_str(context, DC_CONTACT_ID_SELF)
.stock_system_msg(StockMessage::MsgGrpImgChanged, "", "", DC_CONTACT_ID_SELF) .await
.await, .to_string(),
); );
} }
chat.update_param(context).await?; chat.update_param(context).await?;
@@ -3215,7 +3206,7 @@ mod tests {
assert!(chat.visibility == ChatVisibility::Normal); assert!(chat.visibility == ChatVisibility::Normal);
assert!(!chat.is_device_talk()); assert!(!chat.is_device_talk());
assert!(chat.can_send()); assert!(chat.can_send());
assert_eq!(chat.name, t.stock_str(StockMessage::SavedMessages).await); assert_eq!(chat.name, SavedMessages::stock_str(&t).await);
assert!(chat.get_profile_image(&t).await.is_some()); assert!(chat.get_profile_image(&t).await.is_some());
} }
@@ -3231,7 +3222,7 @@ mod tests {
assert!(chat.visibility == ChatVisibility::Normal); assert!(chat.visibility == ChatVisibility::Normal);
assert!(!chat.is_device_talk()); assert!(!chat.is_device_talk());
assert!(!chat.can_send()); assert!(!chat.can_send());
assert_eq!(chat.name, t.stock_str(StockMessage::DeadDrop).await); assert_eq!(chat.name, DeadDrop::stock_str(&t).await);
} }
#[async_std::test] #[async_std::test]
@@ -3308,7 +3299,7 @@ mod tests {
assert!(chat.is_device_talk()); assert!(chat.is_device_talk());
assert!(!chat.is_self_talk()); assert!(!chat.is_self_talk());
assert!(!chat.can_send()); assert!(!chat.can_send());
assert_eq!(chat.name, t.stock_str(StockMessage::DeviceMessages).await); assert_eq!(chat.name, DeviceMessages::stock_str(&t).await);
assert!(chat.get_profile_image(&t).await.is_some()); assert!(chat.get_profile_image(&t).await.is_some());
// delete device message, make sure it is not added again // delete device message, make sure it is not added again

View File

@@ -14,7 +14,7 @@ use crate::context::Context;
use crate::ephemeral::delete_expired_messages; use crate::ephemeral::delete_expired_messages;
use crate::lot::Lot; use crate::lot::Lot;
use crate::message::{Message, MessageState, MsgId}; use crate::message::{Message, MessageState, MsgId};
use crate::stock::StockMessage; use crate::stock::NoMessages;
/// An object representing a single chatlist in memory. /// An object representing a single chatlist in memory.
/// ///
@@ -385,12 +385,7 @@ impl Chatlist {
ret.text2 = None; ret.text2 = None;
} else if lastmsg.is_none() || lastmsg.as_ref().unwrap().from_id == DC_CONTACT_ID_UNDEFINED } else if lastmsg.is_none() || lastmsg.as_ref().unwrap().from_id == DC_CONTACT_ID_UNDEFINED
{ {
ret.text2 = Some( ret.text2 = Some(NoMessages::stock_str(context).await.to_string());
context
.stock_str(StockMessage::NoMessages)
.await
.to_string(),
);
} else { } else {
ret.fill(&mut lastmsg.unwrap(), chat, lastcontact.as_ref(), context) ret.fill(&mut lastmsg.unwrap(), chat, lastcontact.as_ref(), context)
.await; .await;
@@ -445,6 +440,7 @@ mod tests {
use crate::chat::{create_group_chat, ProtectionStatus}; use crate::chat::{create_group_chat, ProtectionStatus};
use crate::constants::Viewtype; use crate::constants::Viewtype;
use crate::stock::StockMessage;
use crate::test_utils::TestContext; use crate::test_utils::TestContext;
#[async_std::test] #[async_std::test]

View File

@@ -3,6 +3,7 @@
use strum::{EnumProperty, IntoEnumIterator}; use strum::{EnumProperty, IntoEnumIterator};
use strum_macros::{AsRefStr, Display, EnumIter, EnumProperty, EnumString}; use strum_macros::{AsRefStr, Display, EnumIter, EnumProperty, EnumString};
use crate::blob::BlobObject;
use crate::chat::ChatId; use crate::chat::ChatId;
use crate::constants::DC_VERSION_STR; use crate::constants::DC_VERSION_STR;
use crate::context::Context; use crate::context::Context;
@@ -11,11 +12,8 @@ use crate::events::EventType;
use crate::job; use crate::job;
use crate::message::MsgId; use crate::message::MsgId;
use crate::mimefactory::RECOMMENDED_FILE_SIZE; use crate::mimefactory::RECOMMENDED_FILE_SIZE;
use crate::stock::StockMessage; use crate::provider::{get_provider_by_id, Provider};
use crate::{ use crate::stock::StatusLine;
blob::BlobObject,
provider::{get_provider_by_id, Provider},
};
/// The available configuration keys. /// The available configuration keys.
#[derive( #[derive(
@@ -175,7 +173,7 @@ impl Context {
// Default values // Default values
match key { match key {
Config::Selfstatus => Some(self.stock_str(StockMessage::StatusLine).await.into_owned()), Config::Selfstatus => Some(StatusLine::stock_str(self).await.into_owned()),
Config::ConfiguredInboxFolder => Some("INBOX".to_owned()), Config::ConfiguredInboxFolder => Some("INBOX".to_owned()),
_ => key.get_str("default").map(|s| s.to_string()), _ => key.get_str("default").map(|s| s.to_string()),
} }
@@ -260,7 +258,7 @@ impl Context {
} }
} }
Config::Selfstatus => { Config::Selfstatus => {
let def = self.stock_str(StockMessage::StatusLine).await; let def = StatusLine::stock_str(self).await;
let val = if value.is_none() || value.unwrap() == def { let val = if value.is_none() || value.unwrap() == def {
None None
} else { } else {

View File

@@ -19,7 +19,7 @@ use crate::message::Message;
use crate::oauth2::dc_get_oauth2_addr; use crate::oauth2::dc_get_oauth2_addr;
use crate::provider::{Protocol, Socket, UsernamePattern}; use crate::provider::{Protocol, Socket, UsernamePattern};
use crate::smtp::Smtp; use crate::smtp::Smtp;
use crate::stock::StockMessage; use crate::stock::{ConfigurationFailed, ErrorNoNetwork};
use crate::{chat, e2ee, provider}; use crate::{chat, e2ee, provider};
use crate::{config::Config, dc_tools::time}; use crate::{config::Config, dc_tools::time};
use crate::{ use crate::{
@@ -127,12 +127,14 @@ impl Context {
self, self,
0, 0,
Some( Some(
self.stock_string_repl_str( ConfigurationFailed::stock_str(
StockMessage::ConfigurationFailed, self,
// We are using Anyhow's .context() and to show the inner error, too, we need the {:#}: // We are using Anyhow's .context() and to show the
// inner error, too, we need the {:#}:
format!("{:#}", err), format!("{:#}", err),
) )
.await .await
.to_string()
) )
); );
Err(err) Err(err)
@@ -589,10 +591,7 @@ async fn nicer_configuration_error(context: &Context, errors: Vec<ConfigurationE
.iter() .iter()
.all(|e| e.msg.to_lowercase().contains("could not resolve")) .all(|e| e.msg.to_lowercase().contains("could not resolve"))
{ {
return context return ErrorNoNetwork::stock_str(context).await.to_string();
.stock_str(StockMessage::ErrorNoNetwork)
.await
.to_string();
} }
if errors.iter().all(|e| e.msg == first_err.msg) { if errors.iter().all(|e| e.msg == first_err.msg) {

View File

@@ -25,7 +25,7 @@ use crate::message::MessageState;
use crate::mimeparser::AvatarAction; use crate::mimeparser::AvatarAction;
use crate::param::{Param, Params}; use crate::param::{Param, Params};
use crate::peerstate::{Peerstate, PeerstateVerifiedStatus}; use crate::peerstate::{Peerstate, PeerstateVerifiedStatus};
use crate::stock::StockMessage; use crate::stock::{DeviceMessages, E2eAvailable, E2ePreferred, EncrNone, FingerPrints, SelfMsg};
/// An object representing a single contact in memory. /// An object representing a single contact in memory.
/// ///
@@ -195,7 +195,7 @@ impl Contact {
) )
.await?; .await?;
if contact_id == DC_CONTACT_ID_SELF { if contact_id == DC_CONTACT_ID_SELF {
res.name = context.stock_str(StockMessage::SelfMsg).await.to_string(); res.name = SelfMsg::stock_str(context).await.to_string();
res.addr = context res.addr = context
.get_config(Config::ConfiguredAddr) .get_config(Config::ConfiguredAddr)
.await .await
@@ -205,10 +205,7 @@ impl Contact {
.await .await
.unwrap_or_default(); .unwrap_or_default();
} else if contact_id == DC_CONTACT_ID_DEVICE { } else if contact_id == DC_CONTACT_ID_DEVICE {
res.name = context res.name = DeviceMessages::stock_str(context).await.to_string();
.stock_str(StockMessage::DeviceMessages)
.await
.to_string();
res.addr = DC_CONTACT_ID_DEVICE_ADDR.to_string(); res.addr = DC_CONTACT_ID_DEVICE_ADDR.to_string();
} }
Ok(res) Ok(res)
@@ -635,7 +632,7 @@ impl Contact {
.get_config(Config::Displayname) .get_config(Config::Displayname)
.await .await
.unwrap_or_default(); .unwrap_or_default();
let self_name2 = context.stock_str(StockMessage::SelfMsg); let self_name2 = SelfMsg::stock_str(context);
if let Some(query) = query { if let Some(query) = query {
if self_addr.contains(query.as_ref()) if self_addr.contains(query.as_ref())
@@ -729,15 +726,15 @@ impl Contact {
.is_some() .is_some()
}) { }) {
let stock_message = match peerstate.prefer_encrypt { let stock_message = match peerstate.prefer_encrypt {
EncryptPreference::Mutual => StockMessage::E2ePreferred, EncryptPreference::Mutual => E2ePreferred::stock_str(context).await,
EncryptPreference::NoPreference => StockMessage::E2eAvailable, EncryptPreference::NoPreference => E2eAvailable::stock_str(context).await,
EncryptPreference::Reset => StockMessage::EncrNone, EncryptPreference::Reset => EncrNone::stock_str(context).await,
}; };
ret += &format!( ret += &format!(
"{}\n{}:", "{}\n{}:",
context.stock_str(stock_message).await, stock_message,
context.stock_str(StockMessage::FingerPrints).await FingerPrints::stock_str(context).await
); );
let fingerprint_self = SignedPublicKey::load_self(context) let fingerprint_self = SignedPublicKey::load_self(context)
@@ -770,7 +767,7 @@ impl Contact {
cat_fingerprint(&mut ret, &loginparam.addr, &fingerprint_self, ""); cat_fingerprint(&mut ret, &loginparam.addr, &fingerprint_self, "");
} }
} else { } else {
ret += &context.stock_str(StockMessage::EncrNone).await; ret += &EncrNone::stock_str(context).await;
} }
} }
@@ -1510,7 +1507,7 @@ mod tests {
// check SELF // check SELF
let contact = Contact::load_from_db(&t, DC_CONTACT_ID_SELF).await.unwrap(); let contact = Contact::load_from_db(&t, DC_CONTACT_ID_SELF).await.unwrap();
assert_eq!(DC_CONTACT_ID_SELF, 1); assert_eq!(DC_CONTACT_ID_SELF, 1);
assert_eq!(contact.get_name(), t.stock_str(StockMessage::SelfMsg).await); assert_eq!(contact.get_name(), SelfMsg::stock_str(&t).await);
assert_eq!(contact.get_addr(), ""); // we're not configured assert_eq!(contact.get_addr(), ""); // we're not configured
assert!(!contact.is_blocked()); assert!(!contact.is_blocked());
} }

View File

@@ -26,7 +26,10 @@ use crate::mimeparser::{parse_message_ids, AvatarAction, MimeMessage, SystemMess
use crate::param::{Param, Params}; use crate::param::{Param, Params};
use crate::peerstate::{Peerstate, PeerstateKeyType, PeerstateVerifiedStatus}; use crate::peerstate::{Peerstate, PeerstateKeyType, PeerstateVerifiedStatus};
use crate::securejoin::{self, handle_securejoin_handshake, observe_securejoin_on_other_device}; use crate::securejoin::{self, handle_securejoin_handshake, observe_securejoin_on_other_device};
use crate::stock::StockMessage; use crate::stock::{
MsgAddMember, MsgDelMember, MsgGroupLeft, MsgGrpImgChanged, MsgGrpImgDeleted, MsgGrpName,
MsgLocationEnabled, UnknownSenderForChat,
};
use crate::{contact, location}; use crate::{contact, location};
// IndexSet is like HashSet but maintains order of insertion // IndexSet is like HashSet but maintains order of insertion
@@ -1165,9 +1168,9 @@ async fn create_or_lookup_group(
let mut better_msg: String = From::from(""); let mut better_msg: String = From::from("");
if mime_parser.is_system_message == SystemMessage::LocationStreamingEnabled { if mime_parser.is_system_message == SystemMessage::LocationStreamingEnabled {
better_msg = context better_msg = MsgLocationEnabled::stock_str_by(context, from_id)
.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", from_id as u32) .await
.await; .to_string();
set_better_msg(mime_parser, &better_msg); set_better_msg(mime_parser, &better_msg);
} }
@@ -1231,48 +1234,38 @@ async fn create_or_lookup_group(
match removed_id { match removed_id {
Some(contact_id) => { Some(contact_id) => {
mime_parser.is_system_message = SystemMessage::MemberRemovedFromGroup; mime_parser.is_system_message = SystemMessage::MemberRemovedFromGroup;
better_msg = context better_msg = if contact_id == from_id {
.stock_system_msg( MsgGroupLeft::stock_str(context, from_id).await.to_string()
if contact_id == from_id as u32 { } else {
StockMessage::MsgGroupLeft MsgDelMember::stock_str(context, &removed_addr, from_id)
} else { .await
StockMessage::MsgDelMember .to_string()
}, };
&removed_addr,
"",
from_id as u32,
)
.await;
} }
None => warn!(context, "removed {:?} has no contact_id", removed_addr), None => warn!(context, "removed {:?} has no contact_id", removed_addr),
} }
} else { } else {
let field = mime_parser.get(HeaderDef::ChatGroupMemberAdded).cloned(); let field = mime_parser.get(HeaderDef::ChatGroupMemberAdded).cloned();
if let Some(optional_field) = field { if let Some(added_member) = field {
mime_parser.is_system_message = SystemMessage::MemberAddedToGroup; mime_parser.is_system_message = SystemMessage::MemberAddedToGroup;
better_msg = context better_msg = MsgAddMember::stock_str(context, &added_member, from_id)
.stock_system_msg( .await
StockMessage::MsgAddMember, .to_string();
&optional_field, X_MrAddToGrp = Some(added_member);
"",
from_id as u32,
)
.await;
X_MrAddToGrp = Some(optional_field);
} else if let Some(old_name) = mime_parser.get(HeaderDef::ChatGroupNameChanged) { } else if let Some(old_name) = mime_parser.get(HeaderDef::ChatGroupNameChanged) {
X_MrGrpNameChanged = true; X_MrGrpNameChanged = true;
better_msg = context better_msg = MsgGrpName::stock_str(
.stock_system_msg( context,
StockMessage::MsgGrpName, old_name,
old_name, if let Some(ref name) = grpname {
if let Some(ref name) = grpname { name
name } else {
} else { ""
"" },
}, from_id as u32,
from_id as u32, )
) .await
.await; .to_string();
mime_parser.is_system_message = SystemMessage::GroupNameChanged; mime_parser.is_system_message = SystemMessage::GroupNameChanged;
} else if let Some(value) = mime_parser.get(HeaderDef::ChatContent) { } else if let Some(value) = mime_parser.get(HeaderDef::ChatContent) {
if value == "group-avatar-changed" { if value == "group-avatar-changed" {
@@ -1280,17 +1273,14 @@ async fn create_or_lookup_group(
// this is just an explicit message containing the group-avatar, // this is just an explicit message containing the group-avatar,
// apart from that, the group-avatar is send along with various other messages // apart from that, the group-avatar is send along with various other messages
mime_parser.is_system_message = SystemMessage::GroupImageChanged; mime_parser.is_system_message = SystemMessage::GroupImageChanged;
better_msg = context better_msg = match avatar_action {
.stock_system_msg( AvatarAction::Delete => MsgGrpImgDeleted::stock_str(context, from_id)
match avatar_action { .await
AvatarAction::Delete => StockMessage::MsgGrpImgDeleted, .to_string(),
AvatarAction::Change(_) => StockMessage::MsgGrpImgChanged, AvatarAction::Change(_) => MsgGrpImgChanged::stock_str(context, from_id)
}, .await
"", .to_string(),
"", };
from_id as u32,
)
.await
} }
} }
} }
@@ -1308,7 +1298,7 @@ async fn create_or_lookup_group(
// but still show the message as part of the chat. // but still show the message as part of the chat.
// After all, the sender has a reference/in-reply-to that // After all, the sender has a reference/in-reply-to that
// points to this chat. // points to this chat.
let s = context.stock_str(StockMessage::UnknownSenderForChat).await; let s = UnknownSenderForChat::stock_str(context).await;
mime_parser.repl_msg_by_error(s.to_string()); mime_parser.repl_msg_by_error(s.to_string());
} }
@@ -1989,6 +1979,7 @@ mod tests {
use crate::constants::{DC_CONTACT_ID_INFO, DC_GCL_NO_SPECIALS}; use crate::constants::{DC_CONTACT_ID_INFO, DC_GCL_NO_SPECIALS};
use crate::message::ContactRequestDecision::*; use crate::message::ContactRequestDecision::*;
use crate::message::Message; use crate::message::Message;
use crate::stock::FailedSendingTo;
use crate::test_utils::TestContext; use crate::test_utils::TestContext;
use crate::{ use crate::{
chat::{ChatItem, ChatVisibility}, chat::{ChatItem, ChatVisibility},
@@ -2656,11 +2647,9 @@ mod tests {
assert_eq!( assert_eq!(
last_msg.text, last_msg.text,
Some( Some(
t.stock_string_repl_str( FailedSendingTo::stock_str(&t, "assidhfaaspocwaeofi@gmail.com")
StockMessage::FailedSendingTo, .await
"assidhfaaspocwaeofi@gmail.com", .to_string(),
)
.await,
) )
); );
assert_eq!(last_msg.from_id, DC_CONTACT_ID_INFO); assert_eq!(last_msg.from_id, DC_CONTACT_ID_INFO);

View File

@@ -22,7 +22,7 @@ use crate::context::Context;
use crate::events::EventType; use crate::events::EventType;
use crate::message::Message; use crate::message::Message;
use crate::provider::get_provider_update_timestamp; use crate::provider::get_provider_update_timestamp;
use crate::stock::StockMessage; use crate::stock::{BadTimeMsgBody, UpdateReminderMsgBody};
/// Shortens a string to a specified length and adds "[...]" to the /// Shortens a string to a specified length and adds "[...]" to the
/// end of the shortened string. /// end of the shortened string.
@@ -169,15 +169,15 @@ async fn maybe_warn_on_bad_time(context: &Context, now: i64, known_past_timestam
if now < known_past_timestamp { if now < known_past_timestamp {
let mut msg = Message::new(Viewtype::Text); let mut msg = Message::new(Viewtype::Text);
msg.text = Some( msg.text = Some(
context BadTimeMsgBody::stock_str(
.stock_string_repl_str( context,
StockMessage::BadTimeMsgBody, Local
Local .timestamp(now, 0)
.timestamp(now, 0) .format("%Y-%m-%d %H:%M:%S")
.format("%Y-%m-%d %H:%M:%S") .to_string(),
.to_string(), )
) .await
.await, .to_string(),
); );
add_device_msg_with_importance( add_device_msg_with_importance(
context, context,
@@ -201,12 +201,7 @@ async fn maybe_warn_on_bad_time(context: &Context, now: i64, known_past_timestam
async fn maybe_warn_on_outdated(context: &Context, now: i64, approx_compile_time: i64) { async fn maybe_warn_on_outdated(context: &Context, now: i64, approx_compile_time: i64) {
if now > approx_compile_time + DC_OUTDATED_WARNING_DAYS * 24 * 60 * 60 { if now > approx_compile_time + DC_OUTDATED_WARNING_DAYS * 24 * 60 * 60 {
let mut msg = Message::new(Viewtype::Text); let mut msg = Message::new(Viewtype::Text);
msg.text = Some( msg.text = Some(UpdateReminderMsgBody::stock_str(context).await.into());
context
.stock_str(StockMessage::UpdateReminderMsgBody)
.await
.into(),
);
add_device_msg( add_device_msg(
context, context,
Some( Some(

View File

@@ -56,7 +56,15 @@
//! the database entries which are expired either according to their //! the database entries which are expired either according to their
//! ephemeral message timers or global `delete_server_after` setting. //! ephemeral message timers or global `delete_server_after` setting.
use std::borrow::Cow;
use std::convert::{TryFrom, TryInto};
use std::num::ParseIntError;
use std::str::FromStr;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use anyhow::{ensure, Error}; use anyhow::{ensure, Error};
use async_std::task;
use serde::{Deserialize, Serialize};
use crate::chat::{lookup_by_contact_id, send_msg, ChatId}; use crate::chat::{lookup_by_contact_id, send_msg, ChatId};
use crate::constants::{ use crate::constants::{
@@ -68,13 +76,12 @@ use crate::events::EventType;
use crate::message::{Message, MessageState, MsgId}; use crate::message::{Message, MessageState, MsgId};
use crate::mimeparser::SystemMessage; use crate::mimeparser::SystemMessage;
use crate::sql; use crate::sql;
use crate::stock::StockMessage; use crate::stock::{
use async_std::task; MsgEphemeralTimerDay, MsgEphemeralTimerDays, MsgEphemeralTimerDisabled,
use serde::{Deserialize, Serialize}; MsgEphemeralTimerEnabled, MsgEphemeralTimerHour, MsgEphemeralTimerHours,
use std::convert::{TryFrom, TryInto}; MsgEphemeralTimerMinute, MsgEphemeralTimerMinutes, MsgEphemeralTimerWeek,
use std::num::ParseIntError; MsgEphemeralTimerWeeks,
use std::str::FromStr; };
use std::time::{Duration, SystemTime, UNIX_EPOCH};
#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] #[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)]
pub enum Timer { pub enum Timer {
@@ -194,7 +201,11 @@ impl ChatId {
} }
self.inner_set_ephemeral_timer(context, timer).await?; self.inner_set_ephemeral_timer(context, timer).await?;
let mut msg = Message::new(Viewtype::Text); let mut msg = Message::new(Viewtype::Text);
msg.text = Some(stock_ephemeral_timer_changed(context, timer, DC_CONTACT_ID_SELF).await); msg.text = Some(
stock_ephemeral_timer_changed(context, timer, DC_CONTACT_ID_SELF)
.await
.to_string(),
);
msg.param.set_cmd(SystemMessage::EphemeralTimerChanged); msg.param.set_cmd(SystemMessage::EphemeralTimerChanged);
if let Err(err) = send_msg(context, self, &mut msg).await { if let Err(err) = send_msg(context, self, &mut msg).await {
error!( error!(
@@ -211,87 +222,48 @@ pub(crate) async fn stock_ephemeral_timer_changed(
context: &Context, context: &Context,
timer: Timer, timer: Timer,
from_id: u32, from_id: u32,
) -> String { ) -> Cow<'static, str> {
match timer { match timer {
Timer::Disabled => { Timer::Disabled => MsgEphemeralTimerDisabled::stock_str(context, from_id).await,
context Timer::Enabled { duration } => match duration {
.stock_system_msg( 0..=59 => {
StockMessage::MsgEphemeralTimerDisabled, MsgEphemeralTimerEnabled::stock_str(context, timer.to_string(), from_id).await
timer.to_string(), }
"", 60 => MsgEphemeralTimerMinute::stock_str(context, from_id).await,
61..=3599 => {
MsgEphemeralTimerMinutes::stock_str(
context,
format!("{}", (f64::from(duration) / 6.0).round() / 10.0),
from_id, from_id,
) )
.await .await
}
Timer::Enabled { duration } => match duration {
0..=59 => {
context
.stock_system_msg(
StockMessage::MsgEphemeralTimerEnabled,
timer.to_string(),
"",
from_id,
)
.await
}
60 => {
context
.stock_system_msg(StockMessage::MsgEphemeralTimerMinute, "", "", from_id)
.await
}
61..=3599 => {
context
.stock_system_msg(
StockMessage::MsgEphemeralTimerMinutes,
format!("{}", (f64::from(duration) / 6.0).round() / 10.0),
"",
from_id,
)
.await
}
3600 => {
context
.stock_system_msg(StockMessage::MsgEphemeralTimerHour, "", "", from_id)
.await
} }
3600 => MsgEphemeralTimerHour::stock_str(context, from_id).await,
3601..=86399 => { 3601..=86399 => {
context MsgEphemeralTimerHours::stock_str(
.stock_system_msg( context,
StockMessage::MsgEphemeralTimerHours, format!("{}", (f64::from(duration) / 360.0).round() / 10.0),
format!("{}", (f64::from(duration) / 360.0).round() / 10.0), from_id,
"", )
from_id, .await
)
.await
}
86400 => {
context
.stock_system_msg(StockMessage::MsgEphemeralTimerDay, "", "", from_id)
.await
} }
86400 => MsgEphemeralTimerDay::stock_str(context, from_id).await,
86401..=604_799 => { 86401..=604_799 => {
context MsgEphemeralTimerDays::stock_str(
.stock_system_msg( context,
StockMessage::MsgEphemeralTimerDays, format!("{}", (f64::from(duration) / 8640.0).round() / 10.0),
format!("{}", (f64::from(duration) / 8640.0).round() / 10.0), from_id,
"", )
from_id, .await
)
.await
}
604_800 => {
{ context.stock_system_msg(StockMessage::MsgEphemeralTimerWeek, "", "", from_id) }
.await
} }
604_800 => MsgEphemeralTimerWeek::stock_str(context, from_id).await,
_ => { _ => {
context MsgEphemeralTimerWeeks::stock_str(
.stock_system_msg( context,
StockMessage::MsgEphemeralTimerWeeks, format!("{}", (f64::from(duration) / 60480.0).round() / 10.0),
format!("{}", (f64::from(duration) / 60480.0).round() / 10.0), from_id,
"", )
from_id, .await
)
.await
} }
}, },
} }
@@ -547,36 +519,67 @@ mod tests {
); );
assert_eq!( assert_eq!(
stock_ephemeral_timer_changed(&context, Timer::Disabled, 0).await, stock_ephemeral_timer_changed(
"Message deletion timer is disabled." &context,
Timer::Enabled { duration: 1 },
DC_CONTACT_ID_SELF
)
.await,
"Message deletion timer is set to 1 s by me."
); );
assert_eq!( assert_eq!(
stock_ephemeral_timer_changed(&context, Timer::Enabled { duration: 1 }, 0).await, stock_ephemeral_timer_changed(
"Message deletion timer is set to 1 s." &context,
Timer::Enabled { duration: 30 },
DC_CONTACT_ID_SELF
)
.await,
"Message deletion timer is set to 30 s by me."
); );
assert_eq!( assert_eq!(
stock_ephemeral_timer_changed(&context, Timer::Enabled { duration: 30 }, 0).await, stock_ephemeral_timer_changed(
"Message deletion timer is set to 30 s." &context,
Timer::Enabled { duration: 60 },
DC_CONTACT_ID_SELF
)
.await,
"Message deletion timer is set to 1 minute by me."
); );
assert_eq!( assert_eq!(
stock_ephemeral_timer_changed(&context, Timer::Enabled { duration: 60 }, 0).await, stock_ephemeral_timer_changed(
"Message deletion timer is set to 1 minute." &context,
Timer::Enabled { duration: 90 },
DC_CONTACT_ID_SELF
)
.await,
"Message deletion timer is set to 1.5 minutes by me."
); );
assert_eq!( assert_eq!(
stock_ephemeral_timer_changed(&context, Timer::Enabled { duration: 90 }, 0).await, stock_ephemeral_timer_changed(
"Message deletion timer is set to 1.5 minutes." &context,
Timer::Enabled { duration: 30 * 60 },
DC_CONTACT_ID_SELF
)
.await,
"Message deletion timer is set to 30 minutes by me."
); );
assert_eq!( assert_eq!(
stock_ephemeral_timer_changed(&context, Timer::Enabled { duration: 30 * 60 }, 0).await, stock_ephemeral_timer_changed(
"Message deletion timer is set to 30 minutes." &context,
Timer::Enabled { duration: 60 * 60 },
DC_CONTACT_ID_SELF
)
.await,
"Message deletion timer is set to 1 hour by me."
); );
assert_eq!( assert_eq!(
stock_ephemeral_timer_changed(&context, Timer::Enabled { duration: 60 * 60 }, 0).await, stock_ephemeral_timer_changed(
"Message deletion timer is set to 1 hour." &context,
); Timer::Enabled { duration: 5400 },
assert_eq!( DC_CONTACT_ID_SELF
stock_ephemeral_timer_changed(&context, Timer::Enabled { duration: 5400 }, 0).await, )
"Message deletion timer is set to 1.5 hours." .await,
"Message deletion timer is set to 1.5 hours by me."
); );
assert_eq!( assert_eq!(
stock_ephemeral_timer_changed( stock_ephemeral_timer_changed(
@@ -584,10 +587,10 @@ mod tests {
Timer::Enabled { Timer::Enabled {
duration: 2 * 60 * 60 duration: 2 * 60 * 60
}, },
0 DC_CONTACT_ID_SELF
) )
.await, .await,
"Message deletion timer is set to 2 hours." "Message deletion timer is set to 2 hours by me."
); );
assert_eq!( assert_eq!(
stock_ephemeral_timer_changed( stock_ephemeral_timer_changed(
@@ -595,10 +598,10 @@ mod tests {
Timer::Enabled { Timer::Enabled {
duration: 24 * 60 * 60 duration: 24 * 60 * 60
}, },
0 DC_CONTACT_ID_SELF
) )
.await, .await,
"Message deletion timer is set to 1 day." "Message deletion timer is set to 1 day by me."
); );
assert_eq!( assert_eq!(
stock_ephemeral_timer_changed( stock_ephemeral_timer_changed(
@@ -606,10 +609,10 @@ mod tests {
Timer::Enabled { Timer::Enabled {
duration: 2 * 24 * 60 * 60 duration: 2 * 24 * 60 * 60
}, },
0 DC_CONTACT_ID_SELF
) )
.await, .await,
"Message deletion timer is set to 2 days." "Message deletion timer is set to 2 days by me."
); );
assert_eq!( assert_eq!(
stock_ephemeral_timer_changed( stock_ephemeral_timer_changed(
@@ -617,10 +620,10 @@ mod tests {
Timer::Enabled { Timer::Enabled {
duration: 7 * 24 * 60 * 60 duration: 7 * 24 * 60 * 60
}, },
0 DC_CONTACT_ID_SELF
) )
.await, .await,
"Message deletion timer is set to 1 week." "Message deletion timer is set to 1 week by me."
); );
assert_eq!( assert_eq!(
stock_ephemeral_timer_changed( stock_ephemeral_timer_changed(
@@ -628,10 +631,10 @@ mod tests {
Timer::Enabled { Timer::Enabled {
duration: 4 * 7 * 24 * 60 * 60 duration: 4 * 7 * 24 * 60 * 60
}, },
0 DC_CONTACT_ID_SELF
) )
.await, .await,
"Message deletion timer is set to 4 weeks." "Message deletion timer is set to 4 weeks by me."
); );
} }

View File

@@ -14,12 +14,17 @@ use async_std::channel::Receiver;
use async_std::prelude::*; use async_std::prelude::*;
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
use crate::chat;
use crate::config::Config;
use crate::constants::{ use crate::constants::{
Chattype, ShowEmails, Viewtype, DC_CONTACT_ID_SELF, DC_FETCH_EXISTING_MSGS_COUNT, Chattype, ShowEmails, Viewtype, DC_CONTACT_ID_SELF, DC_FETCH_EXISTING_MSGS_COUNT,
DC_FOLDERS_CONFIGURED_VERSION, DC_LP_AUTH_OAUTH2, DC_FOLDERS_CONFIGURED_VERSION, DC_LP_AUTH_OAUTH2,
}; };
use crate::context::Context; use crate::context::Context;
use crate::dc_receive_imf::{from_field_to_contact_id, get_prefetch_parent_message}; use crate::dc_receive_imf::{
dc_receive_imf_inner, from_field_to_contact_id, get_prefetch_parent_message,
};
use crate::dc_tools::dc_extract_grpid_from_rfc724_mid;
use crate::events::EventType; use crate::events::EventType;
use crate::headerdef::{HeaderDef, HeaderDefMap}; use crate::headerdef::{HeaderDef, HeaderDefMap};
use crate::job::{self, Action}; use crate::job::{self, Action};
@@ -29,10 +34,8 @@ use crate::mimeparser;
use crate::oauth2::dc_get_oauth2_access_token; use crate::oauth2::dc_get_oauth2_access_token;
use crate::param::Params; use crate::param::Params;
use crate::provider::Socket; use crate::provider::Socket;
use crate::{ use crate::scheduler::InterruptInfo;
chat, dc_tools::dc_extract_grpid_from_rfc724_mid, scheduler::InterruptInfo, stock::StockMessage, use crate::stock::CannotLogin;
};
use crate::{config::Config, dc_receive_imf::dc_receive_imf_inner};
mod client; mod client;
mod idle; mod idle;
@@ -252,9 +255,9 @@ impl Imap {
Err((err, _)) => { Err((err, _)) => {
let imap_user = self.config.lp.user.to_owned(); let imap_user = self.config.lp.user.to_owned();
let message = context let message = CannotLogin::stock_str(context, &imap_user)
.stock_string_repl_str(StockMessage::CannotLogin, &imap_user) .await
.await; .to_string();
warn!(context, "{} ({})", message, err); warn!(context, "{} ({})", message, err);

View File

@@ -29,7 +29,7 @@ use crate::mimeparser::SystemMessage;
use crate::param::Param; use crate::param::Param;
use crate::pgp; use crate::pgp;
use crate::sql::{self, Sql}; use crate::sql::{self, Sql};
use crate::stock::StockMessage; use crate::stock::{AcSetupMsgBody, AcSetupMsgSubject};
use ::pgp::types::KeyTrait; use ::pgp::types::KeyTrait;
use async_tar::Archive; use async_tar::Archive;
@@ -275,8 +275,8 @@ pub async fn render_setup_file(context: &Context, passphrase: &str) -> Result<St
); );
let pgp_msg = encr.replace("-----BEGIN PGP MESSAGE-----", &replacement); let pgp_msg = encr.replace("-----BEGIN PGP MESSAGE-----", &replacement);
let msg_subj = context.stock_str(StockMessage::AcSetupMsgSubject).await; let msg_subj = AcSetupMsgSubject::stock_str(context).await;
let msg_body = context.stock_str(StockMessage::AcSetupMsgBody).await; let msg_body = AcSetupMsgBody::stock_str(context).await;
let msg_body_html = msg_body.replace("\r", "").replace("\n", "<br>"); let msg_body_html = msg_body.replace("\r", "").replace("\n", "<br>");
Ok(format!( Ok(format!(
concat!( concat!(
@@ -902,8 +902,11 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::pgp::{split_armored_data, HEADER_AUTOCRYPT, HEADER_SETUPCODE}; use crate::pgp::{split_armored_data, HEADER_AUTOCRYPT, HEADER_SETUPCODE};
use crate::stock::StockMessage;
use crate::test_utils::{alice_keypair, TestContext}; use crate::test_utils::{alice_keypair, TestContext};
use ::pgp::armor::BlockType; use ::pgp::armor::BlockType;
#[async_std::test] #[async_std::test]

View File

@@ -14,7 +14,7 @@ use crate::job::{self, Job};
use crate::message::{Message, MsgId}; use crate::message::{Message, MsgId};
use crate::mimeparser::SystemMessage; use crate::mimeparser::SystemMessage;
use crate::param::Params; use crate::param::Params;
use crate::stock::StockMessage; use crate::stock::{MsgLocationDisabled, MsgLocationEnabled};
/// Location record /// Location record
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
@@ -212,19 +212,13 @@ pub async fn send_locations_to_chat(context: &Context, chat_id: ChatId, seconds:
{ {
if 0 != seconds && !is_sending_locations_before { if 0 != seconds && !is_sending_locations_before {
let mut msg = Message::new(Viewtype::Text); let mut msg = Message::new(Viewtype::Text);
msg.text = Some( msg.text = Some(MsgLocationEnabled::stock_str(context).await.to_string());
context
.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", 0)
.await,
);
msg.param.set_cmd(SystemMessage::LocationStreamingEnabled); msg.param.set_cmd(SystemMessage::LocationStreamingEnabled);
chat::send_msg(context, chat_id, &mut msg) chat::send_msg(context, chat_id, &mut msg)
.await .await
.unwrap_or_default(); .unwrap_or_default();
} else if 0 == seconds && is_sending_locations_before { } else if 0 == seconds && is_sending_locations_before {
let stock_str = context let stock_str = MsgLocationDisabled::stock_str(context).await;
.stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0)
.await;
chat::add_info_msg(context, chat_id, stock_str).await; chat::add_info_msg(context, chat_id, stock_str).await;
} }
context.emit_event(EventType::ChatModified(chat_id)); context.emit_event(EventType::ChatModified(chat_id));
@@ -716,9 +710,7 @@ pub(crate) async fn job_maybe_send_locations_ended(
paramsv![chat_id], paramsv![chat_id],
).await); ).await);
let stock_str = context let stock_str = MsgLocationDisabled::stock_str(context).await;
.stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0)
.await;
chat::add_info_msg(context, chat_id, stock_str).await; chat::add_info_msg(context, chat_id, stock_str).await;
context.emit_event(EventType::ChatModified(chat_id)); context.emit_event(EventType::ChatModified(chat_id));
} }

View File

@@ -26,7 +26,10 @@ use crate::lot::{Lot, LotState, Meaning};
use crate::mimeparser::{FailureReport, SystemMessage}; use crate::mimeparser::{FailureReport, SystemMessage};
use crate::param::{Param, Params}; use crate::param::{Param, Params};
use crate::pgp::split_armored_data; use crate::pgp::split_armored_data;
use crate::stock::StockMessage; use crate::stock::{
AcSetupMsgSubject, Audio, Draft, FailedSendingTo, File, Gif, Image, Location, ReplyNoun,
SelfMsg, Sticker, Video, VideochatInvitation, VoiceMessage,
};
use std::collections::BTreeMap; use std::collections::BTreeMap;
// In practice, the user additionally cuts the string themselves // In practice, the user additionally cuts the string themselves
@@ -1055,26 +1058,14 @@ impl Lot {
context: &Context, context: &Context,
) { ) {
if msg.state == MessageState::OutDraft { if msg.state == MessageState::OutDraft {
self.text1 = Some( self.text1 = Some(Draft::stock_str(context).await.to_owned().into());
context
.stock_str(StockMessage::Draft)
.await
.to_owned()
.into(),
);
self.text1_meaning = Meaning::Text1Draft; self.text1_meaning = Meaning::Text1Draft;
} else if msg.from_id == DC_CONTACT_ID_SELF { } else if msg.from_id == DC_CONTACT_ID_SELF {
if msg.is_info() || chat.is_self_talk() { if msg.is_info() || chat.is_self_talk() {
self.text1 = None; self.text1 = None;
self.text1_meaning = Meaning::None; self.text1_meaning = Meaning::None;
} else { } else {
self.text1 = Some( self.text1 = Some(SelfMsg::stock_str(context).await.to_owned().into());
context
.stock_str(StockMessage::SelfMsg)
.await
.to_owned()
.into(),
);
self.text1_meaning = Meaning::Text1Self; self.text1_meaning = Meaning::Text1Self;
} }
} else { } else {
@@ -1107,10 +1098,7 @@ impl Lot {
.await; .await;
if text2.is_empty() && msg.quoted_text().is_some() { if text2.is_empty() && msg.quoted_text().is_some() {
text2 = context text2 = ReplyNoun::stock_str(context).await.into_owned()
.stock_str(StockMessage::ReplyNoun)
.await
.into_owned()
} }
self.text2 = Some(text2); self.text2 = Some(text2);
@@ -1562,21 +1550,15 @@ pub async fn get_summarytext_by_raw(
) -> String { ) -> String {
let mut append_text = true; let mut append_text = true;
let prefix = match viewtype { let prefix = match viewtype {
Viewtype::Image => context.stock_str(StockMessage::Image).await.into_owned(), Viewtype::Image => Image::stock_str(context).await.into_owned(),
Viewtype::Gif => context.stock_str(StockMessage::Gif).await.into_owned(), Viewtype::Gif => Gif::stock_str(context).await.into_owned(),
Viewtype::Sticker => context.stock_str(StockMessage::Sticker).await.into_owned(), Viewtype::Sticker => Sticker::stock_str(context).await.into_owned(),
Viewtype::Video => context.stock_str(StockMessage::Video).await.into_owned(), Viewtype::Video => Video::stock_str(context).await.into_owned(),
Viewtype::Voice => context Viewtype::Voice => VoiceMessage::stock_str(context).await.into_owned(),
.stock_str(StockMessage::VoiceMessage)
.await
.into_owned(),
Viewtype::Audio | Viewtype::File => { Viewtype::Audio | Viewtype::File => {
if param.get_cmd() == SystemMessage::AutocryptSetupMessage { if param.get_cmd() == SystemMessage::AutocryptSetupMessage {
append_text = false; append_text = false;
context AcSetupMsgSubject::stock_str(context).await.to_string()
.stock_str(StockMessage::AcSetupMsgSubject)
.await
.to_string()
} else { } else {
let file_name: String = param let file_name: String = param
.get_path(Param::File, context) .get_path(Param::File, context)
@@ -1586,29 +1568,24 @@ pub async fn get_summarytext_by_raw(
.map(|fname| fname.to_string_lossy().into_owned()) .map(|fname| fname.to_string_lossy().into_owned())
}) })
.unwrap_or_else(|| String::from("ErrFileName")); .unwrap_or_else(|| String::from("ErrFileName"));
let label = context let label = if viewtype == Viewtype::Audio {
.stock_str(if viewtype == Viewtype::Audio { Audio::stock_str(context).await
StockMessage::Audio } else {
} else { File::stock_str(context).await
StockMessage::File };
})
.await;
format!("{} {}", label, file_name) format!("{} {}", label, file_name)
} }
} }
Viewtype::VideochatInvitation => { Viewtype::VideochatInvitation => {
append_text = false; append_text = false;
context VideochatInvitation::stock_str(context).await.into_owned()
.stock_str(StockMessage::VideochatInvitation)
.await
.into_owned()
} }
_ => { _ => {
if param.get_cmd() != SystemMessage::LocationOnly { if param.get_cmd() != SystemMessage::LocationOnly {
"".to_string() "".to_string()
} else { } else {
append_text = false; append_text = false;
context.stock_str(StockMessage::Location).await.to_string() Location::stock_str(context).await.to_string()
} }
} }
}; };
@@ -1865,13 +1842,9 @@ async fn ndn_maybe_add_info_msg(
Error::msg("ndn_maybe_add_info_msg: Contact ID not found") Error::msg("ndn_maybe_add_info_msg: Contact ID not found")
})?; })?;
let contact = Contact::load_from_db(context, contact_id).await?; let contact = Contact::load_from_db(context, contact_id).await?;
// Tell the user which of the recipients failed if we know that (because in a group, this might otherwise be unclear) // Tell the user which of the recipients failed if we know that (because in
let text = context // a group, this might otherwise be unclear)
.stock_string_repl_str( let text = FailedSendingTo::stock_str(context, contact.get_display_name()).await;
StockMessage::FailedSendingTo,
contact.get_display_name(),
)
.await;
chat::add_info_msg(context, chat_id, text).await; chat::add_info_msg(context, chat_id, text).await;
context.emit_event(EventType::ChatModified(chat_id)); context.emit_event(EventType::ChatModified(chat_id));
} }

View File

@@ -21,7 +21,10 @@ use crate::mimeparser::SystemMessage;
use crate::param::Param; use crate::param::Param;
use crate::peerstate::{Peerstate, PeerstateVerifiedStatus}; use crate::peerstate::{Peerstate, PeerstateVerifiedStatus};
use crate::simplify::escape_message_footer_marks; use crate::simplify::escape_message_footer_marks;
use crate::stock::StockMessage; use crate::stock::{
AcSetupMsgBody, AcSetupMsgSubject, EncryptedMsg, ReadRcpt, ReadRcptMailBody, StatusLine,
SubjectForNewContact,
};
use std::convert::TryInto; use std::convert::TryInto;
// attachments of 25 mb brutto should work on the majority of providers // attachments of 25 mb brutto should work on the majority of providers
@@ -139,10 +142,7 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
) )
.await?; .await?;
let default_str = context let default_str = StatusLine::stock_str(context).await.to_string();
.stock_str(StockMessage::StatusLine)
.await
.to_string();
let factory = MimeFactory { let factory = MimeFactory {
from_addr, from_addr,
from_displayname, from_displayname,
@@ -180,10 +180,7 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
.get_config(Config::Displayname) .get_config(Config::Displayname)
.await .await
.unwrap_or_default(); .unwrap_or_default();
let default_str = context let default_str = StatusLine::stock_str(context).await.to_string();
.stock_str(StockMessage::StatusLine)
.await
.to_string();
let selfstatus = context let selfstatus = context
.get_config(Config::Selfstatus) .get_config(Config::Selfstatus)
.await .await
@@ -345,8 +342,7 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
match self.loaded { match self.loaded {
Loaded::Message { ref chat } => { Loaded::Message { ref chat } => {
if self.msg.param.get_cmd() == SystemMessage::AutocryptSetupMessage { if self.msg.param.get_cmd() == SystemMessage::AutocryptSetupMessage {
self.context AcSetupMsgSubject::stock_str(self.context)
.stock_str(StockMessage::AcSetupMsgSubject)
.await .await
.into_owned() .into_owned()
} else if chat.typ == Chattype::Group { } else if chat.typ == Chattype::Group {
@@ -390,21 +386,14 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
.unwrap_or_default(), .unwrap_or_default(),
}; };
self.context SubjectForNewContact::stock_str(self.context, self_name)
.stock_string_repl_str(
StockMessage::SubjectForNewContact,
self_name,
)
.await .await
.to_string()
} }
} }
} }
} }
Loaded::MDN { .. } => self Loaded::MDN { .. } => ReadRcpt::stock_str(self.context).await.into_owned(),
.context
.stock_str(StockMessage::ReadRcpt)
.await
.into_owned(),
} }
} }
@@ -808,12 +797,7 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
unprotected_headers unprotected_headers
.push(Header::new("Autocrypt-Setup-Message".into(), "v1".into())); .push(Header::new("Autocrypt-Setup-Message".into(), "v1".into()));
placeholdertext = Some( placeholdertext = Some(AcSetupMsgBody::stock_str(self.context).await.to_string());
self.context
.stock_str(StockMessage::AcSetupMsgBody)
.await
.to_string(),
);
} }
SystemMessage::SecurejoinMessage => { SystemMessage::SecurejoinMessage => {
let msg = &self.msg; let msg = &self.msg;
@@ -1071,17 +1055,11 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
.get_int(Param::GuaranteeE2ee) .get_int(Param::GuaranteeE2ee)
.unwrap_or_default() .unwrap_or_default()
{ {
self.context EncryptedMsg::stock_str(self.context).await.into_owned()
.stock_str(StockMessage::EncryptedMsg)
.await
.into_owned()
} else { } else {
self.msg.get_summarytext(self.context, 32).await self.msg.get_summarytext(self.context, 32).await
}; };
let p2 = self let p2 = ReadRcptMailBody::stock_str(self.context, p1).await;
.context
.stock_string_repl_str(StockMessage::ReadRcptMailBody, p1)
.await;
let message_text = format!("{}\r\n", p2); let message_text = format!("{}\r\n", p2);
message = message.child( message = message.child(
PartBuilder::new() PartBuilder::new()

View File

@@ -3,10 +3,12 @@ use std::future::Future;
use std::pin::Pin; use std::pin::Pin;
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use charset::Charset;
use deltachat_derive::{FromSql, ToSql}; use deltachat_derive::{FromSql, ToSql};
use lettre_email::mime::{self, Mime}; use lettre_email::mime::{self, Mime};
use mailparse::{addrparse_header, DispositionType, MailHeader, MailHeaderMap, SingleInfo}; use mailparse::{addrparse_header, DispositionType, MailHeader, MailHeaderMap, SingleInfo};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use percent_encoding::percent_decode_str;
use crate::aheader::Aheader; use crate::aheader::Aheader;
use crate::blob::BlobObject; use crate::blob::BlobObject;
@@ -25,9 +27,7 @@ use crate::message;
use crate::param::{Param, Params}; use crate::param::{Param, Params};
use crate::peerstate::Peerstate; use crate::peerstate::Peerstate;
use crate::simplify::simplify; use crate::simplify::simplify;
use crate::stock::StockMessage; use crate::stock::CantDecryptMsgBody;
use charset::Charset;
use percent_encoding::percent_decode_str;
/// A parsed MIME message. /// A parsed MIME message.
/// ///
@@ -629,7 +629,7 @@ impl MimeMessage {
// we currently do not try to decrypt non-autocrypt messages // we currently do not try to decrypt non-autocrypt messages
// at all. If we see an encrypted part, we set // at all. If we see an encrypted part, we set
// decrypting_failed. // decrypting_failed.
let msg_body = context.stock_str(StockMessage::CantDecryptMsgBody).await; let msg_body = CantDecryptMsgBody::stock_str(context).await;
let txt = format!("[{}]", msg_body); let txt = format!("[{}]", msg_body);
let part = Part { let part = Part {

View File

@@ -13,7 +13,7 @@ use crate::context::Context;
use crate::events::EventType; use crate::events::EventType;
use crate::key::{DcKey, Fingerprint, SignedPublicKey}; use crate::key::{DcKey, Fingerprint, SignedPublicKey};
use crate::sql::Sql; use crate::sql::Sql;
use crate::stock::StockMessage; use crate::stock::ContactSetupChanged;
#[derive(Debug)] #[derive(Debug)]
pub enum PeerstateKeyType { pub enum PeerstateKeyType {
@@ -281,9 +281,7 @@ impl<'a> Peerstate<'a> {
.await .await
.unwrap_or_default(); .unwrap_or_default();
let msg = context let msg = ContactSetupChanged::stock_str(context, self.addr.clone()).await;
.stock_string_repl_str(StockMessage::ContactSetupChanged, self.addr.clone())
.await;
chat::add_info_msg(context, contact_chat_id, msg).await; chat::add_info_msg(context, contact_chat_id, msg).await;
emit_event!(context, EventType::ChatModified(contact_chat_id)); emit_event!(context, EventType::ChatModified(contact_chat_id));

View File

@@ -23,7 +23,7 @@ use crate::param::Param;
use crate::peerstate::{Peerstate, PeerstateKeyType, PeerstateVerifiedStatus, ToSave}; use crate::peerstate::{Peerstate, PeerstateKeyType, PeerstateVerifiedStatus, ToSave};
use crate::qr::check_qr; use crate::qr::check_qr;
use crate::sql; use crate::sql;
use crate::stock::StockMessage; use crate::stock::{ContactNotVerified, ContactVerified};
use crate::token; use crate::token;
mod bobstate; mod bobstate;
@@ -822,10 +822,8 @@ async fn secure_connection_established(context: &Context, contact_chat_id: ChatI
} else { } else {
"?" "?"
}; };
let msg = context let msg = ContactVerified::stock_str(context, addr).await;
.stock_string_repl_str(StockMessage::ContactVerified, addr) chat::add_info_msg(context, contact_chat_id, msg).await;
.await;
chat::add_info_msg(context, contact_chat_id, &msg).await;
emit_event!(context, EventType::ChatModified(contact_chat_id)); emit_event!(context, EventType::ChatModified(contact_chat_id));
info!(context, "StockMessage::ContactVerified posted to 1:1 chat"); info!(context, "StockMessage::ContactVerified posted to 1:1 chat");
} }
@@ -837,16 +835,15 @@ async fn could_not_establish_secure_connection(
) { ) {
let contact_id = chat_id_2_contact_id(context, contact_chat_id).await; let contact_id = chat_id_2_contact_id(context, contact_chat_id).await;
let contact = Contact::get_by_id(context, contact_id).await; let contact = Contact::get_by_id(context, contact_id).await;
let msg = context let msg = ContactNotVerified::stock_str(
.stock_string_repl_str( context,
StockMessage::ContactNotVerified, if let Ok(ref contact) = contact {
if let Ok(ref contact) = contact { contact.get_addr()
contact.get_addr() } else {
} else { "?"
"?" },
}, )
) .await;
.await;
chat::add_info_msg(context, contact_chat_id, &msg).await; chat::add_info_msg(context, contact_chat_id, &msg).await;
error!( error!(

View File

@@ -13,7 +13,7 @@ use crate::events::EventType;
use crate::login_param::{dc_build_tls, CertificateChecks, LoginParam, ServerLoginParam}; use crate::login_param::{dc_build_tls, CertificateChecks, LoginParam, ServerLoginParam};
use crate::oauth2::dc_get_oauth2_access_token; use crate::oauth2::dc_get_oauth2_access_token;
use crate::provider::Socket; use crate::provider::Socket;
use crate::stock::StockMessage; use crate::stock::ServerResponse;
/// SMTP write and read timeout in seconds. /// SMTP write and read timeout in seconds.
const SMTP_TIMEOUT: u64 = 30; const SMTP_TIMEOUT: u64 = 30;
@@ -111,13 +111,13 @@ impl Smtp {
) )
.await; .await;
if let Err(ref err) = res { if let Err(ref err) = res {
let message = context let message = ServerResponse::stock_str(
.stock_string_repl_str2( context,
StockMessage::ServerResponse, format!("SMTP {}:{}", lp.smtp.server, lp.smtp.port),
format!("SMTP {}:{}", lp.smtp.server, lp.smtp.port), err.to_string(),
err.to_string(), )
) .await
.await; .to_string();
context.emit_event(EventType::ErrorNetwork(message)); context.emit_event(EventType::ErrorNetwork(message));
}; };

View File

@@ -10,22 +10,19 @@ use std::time::Duration;
use anyhow::format_err; use anyhow::format_err;
use rusqlite::{Connection, Error as SqlError, OpenFlags}; use rusqlite::{Connection, Error as SqlError, OpenFlags};
use crate::chat::add_device_msg; use crate::chat::{add_device_msg, update_device_icon, update_saved_messages_icon};
use crate::config::Config;
use crate::config::Config::DeleteServerAfter; use crate::config::Config::DeleteServerAfter;
use crate::constants::{ShowEmails, DC_CHAT_ID_TRASH}; use crate::constants::{ShowEmails, Viewtype, DC_CHAT_ID_TRASH};
use crate::context::Context; use crate::context::Context;
use crate::dc_tools::{dc_delete_file, time, EmailAddress}; use crate::dc_tools::{dc_delete_file, time, EmailAddress};
use crate::ephemeral::start_ephemeral_timers; use crate::ephemeral::start_ephemeral_timers;
use crate::imap; use crate::imap;
use crate::message::Message;
use crate::param::{Param, Params}; use crate::param::{Param, Params};
use crate::peerstate::Peerstate; use crate::peerstate::Peerstate;
use crate::provider::get_provider_by_domain; use crate::provider::get_provider_by_domain;
use crate::stock::StockMessage; use crate::stock::DeleteServerTurnedOff;
use crate::{
chat::{update_device_icon, update_saved_messages_icon},
config::Config,
};
use crate::{constants::Viewtype, message::Message};
#[macro_export] #[macro_export]
macro_rules! paramsv { macro_rules! paramsv {
@@ -1544,12 +1541,7 @@ CREATE INDEX devmsglabels_index1 ON devmsglabels (label);
// So, for people who have delete_server enabled, disable it and add a hint to the devicechat: // So, for people who have delete_server enabled, disable it and add a hint to the devicechat:
if context.get_config_delete_server_after().await.is_some() { if context.get_config_delete_server_after().await.is_some() {
let mut msg = Message::new(Viewtype::Text); let mut msg = Message::new(Viewtype::Text);
msg.text = Some( msg.text = Some(DeleteServerTurnedOff::stock_str(context).await.into());
context
.stock_str(StockMessage::DeleteServerTurnedOff)
.await
.into(),
);
add_device_msg(context, None, Some(&mut msg)).await?; add_device_msg(context, None, Some(&mut msg)).await?;
context.set_config(DeleteServerAfter, Some("0")).await?; context.set_config(DeleteServerAfter, Some("0")).await?;
} }

File diff suppressed because it is too large Load Diff