mirror of
https://github.com/chatmail/core.git
synced 2026-04-17 21:46:35 +03:00
feat: get contact-id for info messages (#6714)
instead of showing addresses in info message, provide an API to get the contact-id. UI can then make the info message tappable and open the contact profile in scope the corresponding iOS PR - incl. **screencast** - is at https://github.com/deltachat/deltachat-ios/pull/2652 ; jsonrpc can come in a subsequent PR when things are settled on android/ios the number of parameters in `add_info_msg_with_cmd` gets bigger and bigger, however, i did not want to refactor this in this PR. it is also not really adding complexity closes #6702 --------- Co-authored-by: link2xt <link2xt@testrun.org> Co-authored-by: Hocuri <hocuri@gmx.de>
This commit is contained in:
@@ -4483,6 +4483,11 @@ int dc_msg_is_info (const dc_msg_t* msg);
|
|||||||
* UIs can display e.g. an icon based upon the type.
|
* UIs can display e.g. an icon based upon the type.
|
||||||
*
|
*
|
||||||
* Currently, the following types are defined:
|
* Currently, the following types are defined:
|
||||||
|
* - DC_INFO_GROUP_NAME_CHANGED (2) - "Group name changd from OLD to BY by CONTACT"
|
||||||
|
* - DC_INFO_GROUP_IMAGE_CHANGED (3) - "Group image changd by CONTACT"
|
||||||
|
* - DC_INFO_MEMBER_ADDED_TO_GROUP (4) - "Member CONTACT added by OTHER_CONTACT"
|
||||||
|
* - DC_INFO_MEMBER_REMOVED_FROM_GROUP (5) - "Member CONTACT removed by OTHER_CONTACT"
|
||||||
|
* - DC_INFO_EPHEMERAL_TIMER_CHANGED (10) - "Disappearing messages CHANGED_TO by CONTACT"
|
||||||
* - DC_INFO_PROTECTION_ENABLED (11) - Info-message for "Chat is now protected"
|
* - DC_INFO_PROTECTION_ENABLED (11) - Info-message for "Chat is now protected"
|
||||||
* - DC_INFO_PROTECTION_DISABLED (12) - Info-message for "Chat is no longer protected"
|
* - DC_INFO_PROTECTION_DISABLED (12) - Info-message for "Chat is no longer protected"
|
||||||
* - DC_INFO_INVALID_UNENCRYPTED_MAIL (13) - Info-message for "Provider requires end-to-end encryption which is not setup yet",
|
* - DC_INFO_INVALID_UNENCRYPTED_MAIL (13) - Info-message for "Provider requires end-to-end encryption which is not setup yet",
|
||||||
@@ -4490,6 +4495,10 @@ int dc_msg_is_info (const dc_msg_t* msg);
|
|||||||
* and also offer a way to fix the encryption, eg. by a button offering a QR scan
|
* and also offer a way to fix the encryption, eg. by a button offering a QR scan
|
||||||
* - DC_INFO_WEBXDC_INFO_MESSAGE (32) - Info-message created by webxdc app sending `update.info`
|
* - DC_INFO_WEBXDC_INFO_MESSAGE (32) - Info-message created by webxdc app sending `update.info`
|
||||||
*
|
*
|
||||||
|
* For the messages that refer to a CONTACT,
|
||||||
|
* dc_msg_get_info_contact_id() returns the contact ID.
|
||||||
|
* The UI should open the contact's profile when tapping the info message.
|
||||||
|
*
|
||||||
* Even when you display an icon,
|
* Even when you display an icon,
|
||||||
* you should still display the text of the informational message using dc_msg_get_text()
|
* you should still display the text of the informational message using dc_msg_get_text()
|
||||||
*
|
*
|
||||||
@@ -4502,6 +4511,29 @@ int dc_msg_is_info (const dc_msg_t* msg);
|
|||||||
int dc_msg_get_info_type (const dc_msg_t* msg);
|
int dc_msg_get_info_type (const dc_msg_t* msg);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the contact ID of the profile to open when tapping the info message.
|
||||||
|
*
|
||||||
|
* - For DC_INFO_MEMBER_ADDED_TO_GROUP and DC_INFO_MEMBER_REMOVED_FROM_GROUP,
|
||||||
|
* this is the contact being added/removed.
|
||||||
|
* The contact that did the adding/removal is usually only a tap away
|
||||||
|
* (as introducer and/or atop of the memberlist),
|
||||||
|
* and usually more known anyways.
|
||||||
|
* - For DC_INFO_GROUP_NAME_CHANGED, DC_INFO_GROUP_IMAGE_CHANGED and DC_INFO_EPHEMERAL_TIMER_CHANGED
|
||||||
|
* this is the contact who did the change.
|
||||||
|
*
|
||||||
|
* No need to check additionally for dc_msg_get_info_type(),
|
||||||
|
* unless you e.g. want to show the info message in another style.
|
||||||
|
*
|
||||||
|
* @memberof dc_msg_t
|
||||||
|
* @param msg The message object.
|
||||||
|
* @return If the info message refers to a contact,
|
||||||
|
* this contact ID or DC_CONTACT_ID_SELF is returned.
|
||||||
|
* Otherwise 0.
|
||||||
|
*/
|
||||||
|
uint32_t dc_msg_get_info_contact_id (const dc_msg_t* msg);
|
||||||
|
|
||||||
|
|
||||||
// DC_INFO* uses the same values as SystemMessage in rust-land
|
// DC_INFO* uses the same values as SystemMessage in rust-land
|
||||||
#define DC_INFO_UNKNOWN 0
|
#define DC_INFO_UNKNOWN 0
|
||||||
#define DC_INFO_GROUP_NAME_CHANGED 2
|
#define DC_INFO_GROUP_NAME_CHANGED 2
|
||||||
|
|||||||
@@ -3730,6 +3730,20 @@ pub unsafe extern "C" fn dc_msg_get_info_type(msg: *mut dc_msg_t) -> libc::c_int
|
|||||||
ffi_msg.message.get_info_type() as libc::c_int
|
ffi_msg.message.get_info_type() as libc::c_int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn dc_msg_get_info_contact_id(msg: *mut dc_msg_t) -> u32 {
|
||||||
|
if msg.is_null() {
|
||||||
|
eprintln!("ignoring careless call to dc_msg_get_info_contact_id()");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let ffi_msg = &*msg;
|
||||||
|
let context = &*ffi_msg.context;
|
||||||
|
block_on(ffi_msg.message.get_info_contact_id(context))
|
||||||
|
.unwrap_or_default()
|
||||||
|
.map(|id| id.to_u32())
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn dc_msg_get_webxdc_href(msg: *mut dc_msg_t) -> *mut libc::c_char {
|
pub unsafe extern "C" fn dc_msg_get_webxdc_href(msg: *mut dc_msg_t) -> *mut libc::c_char {
|
||||||
if msg.is_null() {
|
if msg.is_null() {
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ def test_qr_securejoin(acfactory, protect):
|
|||||||
assert alice_contact_bob_snapshot.is_verified
|
assert alice_contact_bob_snapshot.is_verified
|
||||||
|
|
||||||
snapshot = bob.get_message_by_id(bob.wait_for_incoming_msg_event().msg_id).get_snapshot()
|
snapshot = bob.get_message_by_id(bob.wait_for_incoming_msg_event().msg_id).get_snapshot()
|
||||||
assert snapshot.text == "Member Me ({}) added by {}.".format(bob.get_config("addr"), alice.get_config("addr"))
|
assert snapshot.text == "Member Me added by {}.".format(alice.get_config("addr"))
|
||||||
assert snapshot.chat.get_basic_snapshot().is_protected == protect
|
assert snapshot.chat.get_basic_snapshot().is_protected == protect
|
||||||
|
|
||||||
# Test that Bob verified Alice's profile.
|
# Test that Bob verified Alice's profile.
|
||||||
@@ -563,7 +563,7 @@ def test_securejoin_after_contact_resetup(acfactory) -> None:
|
|||||||
|
|
||||||
# ac1 waits for member added message and creates a QR code.
|
# ac1 waits for member added message and creates a QR code.
|
||||||
snapshot = ac1.get_message_by_id(ac1.wait_for_incoming_msg_event().msg_id).get_snapshot()
|
snapshot = ac1.get_message_by_id(ac1.wait_for_incoming_msg_event().msg_id).get_snapshot()
|
||||||
assert snapshot.text == "Member Me ({}) added by {}.".format(ac1.get_config("addr"), ac3.get_config("addr"))
|
assert snapshot.text == "Member Me added by {}.".format(ac3.get_config("addr"))
|
||||||
ac1_qr_code = snapshot.chat.get_qr_code()
|
ac1_qr_code = snapshot.chat.get_qr_code()
|
||||||
|
|
||||||
# ac2 verifies ac1
|
# ac2 verifies ac1
|
||||||
@@ -646,7 +646,7 @@ def test_withdraw_securejoin_qr(acfactory):
|
|||||||
alice.clear_all_events()
|
alice.clear_all_events()
|
||||||
|
|
||||||
snapshot = bob.get_message_by_id(bob.wait_for_incoming_msg_event().msg_id).get_snapshot()
|
snapshot = bob.get_message_by_id(bob.wait_for_incoming_msg_event().msg_id).get_snapshot()
|
||||||
assert snapshot.text == "Member Me ({}) added by {}.".format(bob.get_config("addr"), alice.get_config("addr"))
|
assert snapshot.text == "Member Me added by {}.".format(alice.get_config("addr"))
|
||||||
assert snapshot.chat.get_basic_snapshot().is_protected
|
assert snapshot.chat.get_basic_snapshot().is_protected
|
||||||
bob_chat.leave()
|
bob_chat.leave()
|
||||||
|
|
||||||
|
|||||||
@@ -1346,7 +1346,7 @@ def test_qr_email_capitalization(acfactory, lp):
|
|||||||
lp.sec("ac1 joins a verified group via a QR code")
|
lp.sec("ac1 joins a verified group via a QR code")
|
||||||
ac1_chat = ac1.qr_join_chat(qr)
|
ac1_chat = ac1.qr_join_chat(qr)
|
||||||
msg = ac1._evtracker.wait_next_incoming_message()
|
msg = ac1._evtracker.wait_next_incoming_message()
|
||||||
assert msg.text == "Member Me ({}) added by {}.".format(ac1.get_config("addr"), ac3.get_config("addr"))
|
assert msg.text == "Member Me added by {}.".format(ac3.get_config("addr"))
|
||||||
assert len(ac1_chat.get_contacts()) == 2
|
assert len(ac1_chat.get_contacts()) == 2
|
||||||
|
|
||||||
lp.sec("ac2 joins a verified group via a QR code")
|
lp.sec("ac2 joins a verified group via a QR code")
|
||||||
|
|||||||
25
src/chat.rs
25
src/chat.rs
@@ -582,7 +582,18 @@ impl ChatId {
|
|||||||
ProtectionStatus::Unprotected => SystemMessage::ChatProtectionDisabled,
|
ProtectionStatus::Unprotected => SystemMessage::ChatProtectionDisabled,
|
||||||
ProtectionStatus::ProtectionBroken => SystemMessage::ChatProtectionDisabled,
|
ProtectionStatus::ProtectionBroken => SystemMessage::ChatProtectionDisabled,
|
||||||
};
|
};
|
||||||
add_info_msg_with_cmd(context, self, &text, cmd, timestamp_sort, None, None, None).await?;
|
add_info_msg_with_cmd(
|
||||||
|
context,
|
||||||
|
self,
|
||||||
|
&text,
|
||||||
|
cmd,
|
||||||
|
timestamp_sort,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -1791,6 +1802,7 @@ impl Chat {
|
|||||||
Some(now),
|
Some(now),
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
context.emit_event(EventType::ChatModified(self.id));
|
context.emit_event(EventType::ChatModified(self.id));
|
||||||
@@ -3942,6 +3954,8 @@ pub(crate) async fn add_contact_to_chat_ex(
|
|||||||
msg.param.set_cmd(SystemMessage::MemberAddedToGroup);
|
msg.param.set_cmd(SystemMessage::MemberAddedToGroup);
|
||||||
msg.param.set(Param::Arg, contact_addr);
|
msg.param.set(Param::Arg, contact_addr);
|
||||||
msg.param.set_int(Param::Arg2, from_handshake.into());
|
msg.param.set_int(Param::Arg2, from_handshake.into());
|
||||||
|
msg.param
|
||||||
|
.set_int(Param::ContactAddedRemoved, contact.id.to_u32() as i32);
|
||||||
send_msg(context, chat_id, &mut msg).await?;
|
send_msg(context, chat_id, &mut msg).await?;
|
||||||
|
|
||||||
sync = Nosync;
|
sync = Nosync;
|
||||||
@@ -4139,6 +4153,8 @@ pub async fn remove_contact_from_chat(
|
|||||||
}
|
}
|
||||||
msg.param.set_cmd(SystemMessage::MemberRemovedFromGroup);
|
msg.param.set_cmd(SystemMessage::MemberRemovedFromGroup);
|
||||||
msg.param.set(Param::Arg, contact.get_addr().to_lowercase());
|
msg.param.set(Param::Arg, contact.get_addr().to_lowercase());
|
||||||
|
msg.param
|
||||||
|
.set(Param::ContactAddedRemoved, contact.id.to_u32() as i32);
|
||||||
let res = send_msg(context, chat_id, &mut msg).await;
|
let res = send_msg(context, chat_id, &mut msg).await;
|
||||||
if contact_id == ContactId::SELF {
|
if contact_id == ContactId::SELF {
|
||||||
res?;
|
res?;
|
||||||
@@ -4737,13 +4753,17 @@ pub(crate) async fn add_info_msg_with_cmd(
|
|||||||
timestamp_sent_rcvd: Option<i64>,
|
timestamp_sent_rcvd: Option<i64>,
|
||||||
parent: Option<&Message>,
|
parent: Option<&Message>,
|
||||||
from_id: Option<ContactId>,
|
from_id: Option<ContactId>,
|
||||||
|
added_removed_id: Option<ContactId>,
|
||||||
) -> Result<MsgId> {
|
) -> Result<MsgId> {
|
||||||
let rfc724_mid = create_outgoing_rfc724_mid();
|
let rfc724_mid = create_outgoing_rfc724_mid();
|
||||||
let ephemeral_timer = chat_id.get_ephemeral_timer(context).await?;
|
let ephemeral_timer = chat_id.get_ephemeral_timer(context).await?;
|
||||||
|
|
||||||
let mut param = Params::new();
|
let mut param = Params::new();
|
||||||
if cmd != SystemMessage::Unknown {
|
if cmd != SystemMessage::Unknown {
|
||||||
param.set_cmd(cmd)
|
param.set_cmd(cmd);
|
||||||
|
}
|
||||||
|
if let Some(contact_id) = added_removed_id {
|
||||||
|
param.set(Param::ContactAddedRemoved, contact_id.to_u32().to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
let row_id =
|
let row_id =
|
||||||
@@ -4791,6 +4811,7 @@ pub(crate) async fn add_info_msg(
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::chatlist::get_archived_cnt;
|
use crate::chatlist::get_archived_cnt;
|
||||||
use crate::constants::{DC_GCL_ARCHIVED_ONLY, DC_GCL_NO_SPECIALS};
|
use crate::constants::{DC_GCL_ARCHIVED_ONLY, DC_GCL_NO_SPECIALS};
|
||||||
|
use crate::ephemeral::Timer;
|
||||||
use crate::headerdef::HeaderDef;
|
use crate::headerdef::HeaderDef;
|
||||||
use crate::imex::{has_backup, imex, ImexMode};
|
use crate::imex::{has_backup, imex, ImexMode};
|
||||||
use crate::message::{delete_msgs, MessengerMessage};
|
use crate::message::{delete_msgs, MessengerMessage};
|
||||||
@@ -331,11 +332,12 @@ async fn test_member_add_remove() -> Result<()> {
|
|||||||
// Alice adds Bob to the chat.
|
// Alice adds Bob to the chat.
|
||||||
add_contact_to_chat(&alice, alice_chat_id, alice_bob_contact_id).await?;
|
add_contact_to_chat(&alice, alice_chat_id, alice_bob_contact_id).await?;
|
||||||
let sent = alice.pop_sent_msg().await;
|
let sent = alice.pop_sent_msg().await;
|
||||||
|
|
||||||
// Locally set name "robert" should not leak.
|
// Locally set name "robert" should not leak.
|
||||||
assert!(!sent.payload.contains("robert"));
|
assert!(!sent.payload.contains("robert"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sent.load_from_db().await.get_text(),
|
sent.load_from_db().await.get_text(),
|
||||||
"You added member robert (bob@example.net)."
|
"You added member robert."
|
||||||
);
|
);
|
||||||
|
|
||||||
// Alice removes Bob from the chat.
|
// Alice removes Bob from the chat.
|
||||||
@@ -344,7 +346,7 @@ async fn test_member_add_remove() -> Result<()> {
|
|||||||
assert!(!sent.payload.contains("robert"));
|
assert!(!sent.payload.contains("robert"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sent.load_from_db().await.get_text(),
|
sent.load_from_db().await.get_text(),
|
||||||
"You removed member robert (bob@example.net)."
|
"You removed member robert."
|
||||||
);
|
);
|
||||||
|
|
||||||
// Alice leaves the chat.
|
// Alice leaves the chat.
|
||||||
@@ -412,7 +414,7 @@ async fn test_parallel_member_remove() -> Result<()> {
|
|||||||
// Test that remove message is rewritten.
|
// Test that remove message is rewritten.
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
bob_received_remove_msg.get_text(),
|
bob_received_remove_msg.get_text(),
|
||||||
"Member Me (bob@example.net) removed by alice@example.org."
|
"Member Me removed by alice@example.org."
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -521,6 +523,14 @@ async fn test_modify_chat_multi_device() -> Result<()> {
|
|||||||
assert!(a2_msg.is_system_message());
|
assert!(a2_msg.is_system_message());
|
||||||
assert_eq!(a1_msg.get_info_type(), SystemMessage::GroupNameChanged);
|
assert_eq!(a1_msg.get_info_type(), SystemMessage::GroupNameChanged);
|
||||||
assert_eq!(a2_msg.get_info_type(), SystemMessage::GroupNameChanged);
|
assert_eq!(a2_msg.get_info_type(), SystemMessage::GroupNameChanged);
|
||||||
|
assert_eq!(
|
||||||
|
a1_msg.get_info_contact_id(&a1).await?,
|
||||||
|
Some(ContactId::SELF)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
a2_msg.get_info_contact_id(&a2).await?,
|
||||||
|
Some(ContactId::SELF)
|
||||||
|
);
|
||||||
assert_eq!(Chat::load_from_db(&a1, a1_chat_id).await?.name, "bar");
|
assert_eq!(Chat::load_from_db(&a1, a1_chat_id).await?.name, "bar");
|
||||||
assert_eq!(Chat::load_from_db(&a2, a2_chat_id).await?.name, "bar");
|
assert_eq!(Chat::load_from_db(&a2, a2_chat_id).await?.name, "bar");
|
||||||
|
|
||||||
@@ -1574,6 +1584,7 @@ async fn test_add_info_msg_with_cmd() -> Result<()> {
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@@ -3332,6 +3343,128 @@ async fn test_do_not_overwrite_draft() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
|
async fn test_info_contact_id() -> Result<()> {
|
||||||
|
let mut tcm = TestContextManager::new();
|
||||||
|
let alice = &tcm.alice().await;
|
||||||
|
let alice2 = &tcm.alice().await;
|
||||||
|
let bob = &tcm.bob().await;
|
||||||
|
|
||||||
|
async fn pop_recv_and_check(
|
||||||
|
alice: &TestContext,
|
||||||
|
alice2: &TestContext,
|
||||||
|
bob: &TestContext,
|
||||||
|
expected_type: SystemMessage,
|
||||||
|
expected_alice_id: ContactId,
|
||||||
|
expected_bob_id: ContactId,
|
||||||
|
) -> Result<()> {
|
||||||
|
let sent_msg = alice.pop_sent_msg().await;
|
||||||
|
let msg = Message::load_from_db(alice, sent_msg.sender_msg_id).await?;
|
||||||
|
assert_eq!(msg.get_info_type(), expected_type);
|
||||||
|
assert_eq!(
|
||||||
|
msg.get_info_contact_id(alice).await?,
|
||||||
|
Some(expected_alice_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
let msg = alice2.recv_msg(&sent_msg).await;
|
||||||
|
assert_eq!(msg.get_info_type(), expected_type);
|
||||||
|
assert_eq!(
|
||||||
|
msg.get_info_contact_id(alice2).await?,
|
||||||
|
Some(expected_alice_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
let msg = bob.recv_msg(&sent_msg).await;
|
||||||
|
assert_eq!(msg.get_info_type(), expected_type);
|
||||||
|
assert_eq!(msg.get_info_contact_id(bob).await?, Some(expected_bob_id));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alice creates group, Bob receives group
|
||||||
|
let alice_chat_id = alice
|
||||||
|
.create_group_with_members(ProtectionStatus::Unprotected, "play", &[bob])
|
||||||
|
.await;
|
||||||
|
let sent_msg1 = alice.send_text(alice_chat_id, "moin").await;
|
||||||
|
|
||||||
|
let msg = bob.recv_msg(&sent_msg1).await;
|
||||||
|
let bob_alice_id = msg.from_id;
|
||||||
|
assert!(!bob_alice_id.is_special());
|
||||||
|
|
||||||
|
// Alice does group changes, Bob receives them
|
||||||
|
set_chat_name(alice, alice_chat_id, "games").await?;
|
||||||
|
pop_recv_and_check(
|
||||||
|
alice,
|
||||||
|
alice2,
|
||||||
|
bob,
|
||||||
|
SystemMessage::GroupNameChanged,
|
||||||
|
ContactId::SELF,
|
||||||
|
bob_alice_id,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let file = alice.get_blobdir().join("avatar.png");
|
||||||
|
let bytes = include_bytes!("../../test-data/image/avatar64x64.png");
|
||||||
|
tokio::fs::write(&file, bytes).await?;
|
||||||
|
set_chat_profile_image(alice, alice_chat_id, file.to_str().unwrap()).await?;
|
||||||
|
pop_recv_and_check(
|
||||||
|
alice,
|
||||||
|
alice2,
|
||||||
|
bob,
|
||||||
|
SystemMessage::GroupImageChanged,
|
||||||
|
ContactId::SELF,
|
||||||
|
bob_alice_id,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
alice_chat_id
|
||||||
|
.set_ephemeral_timer(alice, Timer::Enabled { duration: 60 })
|
||||||
|
.await?;
|
||||||
|
pop_recv_and_check(
|
||||||
|
alice,
|
||||||
|
alice2,
|
||||||
|
bob,
|
||||||
|
SystemMessage::EphemeralTimerChanged,
|
||||||
|
ContactId::SELF,
|
||||||
|
bob_alice_id,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let fiona_id = alice.add_or_lookup_contact_id(&tcm.fiona().await).await; // contexts are in sync, fiona_id is same everywhere
|
||||||
|
add_contact_to_chat(alice, alice_chat_id, fiona_id).await?;
|
||||||
|
pop_recv_and_check(
|
||||||
|
alice,
|
||||||
|
alice2,
|
||||||
|
bob,
|
||||||
|
SystemMessage::MemberAddedToGroup,
|
||||||
|
fiona_id,
|
||||||
|
fiona_id,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
remove_contact_from_chat(alice, alice_chat_id, fiona_id).await?;
|
||||||
|
pop_recv_and_check(
|
||||||
|
alice,
|
||||||
|
alice2,
|
||||||
|
bob,
|
||||||
|
SystemMessage::MemberRemovedFromGroup,
|
||||||
|
fiona_id,
|
||||||
|
fiona_id,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// When fiona_id is deleted, get_info_contact_id() returns None.
|
||||||
|
// We raw delete in db as Contact::delete() leaves a tombstone (which is great as the tap works longer then)
|
||||||
|
alice
|
||||||
|
.sql
|
||||||
|
.execute("DELETE FROM contacts WHERE id=?", (fiona_id,))
|
||||||
|
.await?;
|
||||||
|
let msg = alice.get_last_msg().await;
|
||||||
|
assert_eq!(msg.get_info_type(), SystemMessage::MemberRemovedFromGroup);
|
||||||
|
assert!(msg.get_info_contact_id(alice).await?.is_none());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Test group consistency.
|
/// Test group consistency.
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
async fn test_add_member_bug() -> Result<()> {
|
async fn test_add_member_bug() -> Result<()> {
|
||||||
|
|||||||
@@ -1406,16 +1406,13 @@ impl Contact {
|
|||||||
&self.addr
|
&self.addr
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a summary of authorized name and address.
|
/// Get authorized name or address.
|
||||||
///
|
|
||||||
/// The returned string is either "Name (email@domain.com)" or just
|
|
||||||
/// "email@domain.com" if the name is unset.
|
|
||||||
///
|
///
|
||||||
/// This string is suitable for sending over email
|
/// This string is suitable for sending over email
|
||||||
/// as it does not leak the locally set name.
|
/// as it does not leak the locally set name.
|
||||||
pub fn get_authname_n_addr(&self) -> String {
|
pub(crate) fn get_authname_or_addr(&self) -> String {
|
||||||
if !self.authname.is_empty() {
|
if !self.authname.is_empty() {
|
||||||
format!("{} ({})", self.authname, self.addr)
|
(&self.authname).into()
|
||||||
} else {
|
} else {
|
||||||
(&self.addr).into()
|
(&self.addr).into()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,51 @@ impl Message {
|
|||||||
self.param.get_cmd()
|
self.param.get_cmd()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the contact ID of the profile to open when tapping the info message.
|
||||||
|
pub async fn get_info_contact_id(&self, context: &Context) -> Result<Option<ContactId>> {
|
||||||
|
match self.param.get_cmd() {
|
||||||
|
SystemMessage::GroupNameChanged
|
||||||
|
| SystemMessage::GroupImageChanged
|
||||||
|
| SystemMessage::EphemeralTimerChanged => {
|
||||||
|
if self.from_id != ContactId::INFO {
|
||||||
|
Ok(Some(self.from_id))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemMessage::MemberAddedToGroup | SystemMessage::MemberRemovedFromGroup => {
|
||||||
|
if let Some(contact_i32) = self.param.get_int(Param::ContactAddedRemoved) {
|
||||||
|
let contact_id = ContactId::new(contact_i32.try_into()?);
|
||||||
|
if contact_id == ContactId::SELF
|
||||||
|
|| Contact::real_exists_by_id(context, contact_id).await?
|
||||||
|
{
|
||||||
|
Ok(Some(contact_id))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemMessage::AutocryptSetupMessage
|
||||||
|
| SystemMessage::SecurejoinMessage
|
||||||
|
| SystemMessage::LocationStreamingEnabled
|
||||||
|
| SystemMessage::LocationOnly
|
||||||
|
| SystemMessage::ChatProtectionEnabled
|
||||||
|
| SystemMessage::ChatProtectionDisabled
|
||||||
|
| SystemMessage::InvalidUnencryptedMail
|
||||||
|
| SystemMessage::SecurejoinWait
|
||||||
|
| SystemMessage::SecurejoinWaitTimeout
|
||||||
|
| SystemMessage::MultiDeviceSync
|
||||||
|
| SystemMessage::WebxdcStatusUpdate
|
||||||
|
| SystemMessage::WebxdcInfoMessage
|
||||||
|
| SystemMessage::IrohNodeAddr
|
||||||
|
| SystemMessage::Unknown => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns true if the message is a system message.
|
/// Returns true if the message is a system message.
|
||||||
pub fn is_system_message(&self) -> bool {
|
pub fn is_system_message(&self) -> bool {
|
||||||
let cmd = self.param.get_cmd();
|
let cmd = self.param.get_cmd();
|
||||||
|
|||||||
@@ -215,6 +215,9 @@ pub enum Param {
|
|||||||
|
|
||||||
/// For messages: Message text was edited.
|
/// For messages: Message text was edited.
|
||||||
IsEdited = b'L',
|
IsEdited = b'L',
|
||||||
|
|
||||||
|
/// For info messages: Contact ID in added or removed to a group.
|
||||||
|
ContactAddedRemoved = b'5',
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An object for handling key=value parameter lists.
|
/// An object for handling key=value parameter lists.
|
||||||
|
|||||||
@@ -748,6 +748,7 @@ impl Peerstate {
|
|||||||
Some(timestamp),
|
Some(timestamp),
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1451,13 +1451,13 @@ async fn add_parts(
|
|||||||
None => better_msg = Some(m),
|
None => better_msg = Some(m),
|
||||||
Some(_) => {
|
Some(_) => {
|
||||||
if !m.is_empty() {
|
if !m.is_empty() {
|
||||||
group_changes.extra_msgs.push((m, is_system_message))
|
group_changes.extra_msgs.push((m, is_system_message, None))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (group_changes_msg, cmd) in group_changes.extra_msgs {
|
for (group_changes_msg, cmd, added_removed_id) in group_changes.extra_msgs {
|
||||||
chat::add_info_msg_with_cmd(
|
chat::add_info_msg_with_cmd(
|
||||||
context,
|
context,
|
||||||
chat_id,
|
chat_id,
|
||||||
@@ -1467,6 +1467,7 @@ async fn add_parts(
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
|
added_removed_id,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
@@ -1550,6 +1551,10 @@ async fn add_parts(
|
|||||||
let part_is_empty =
|
let part_is_empty =
|
||||||
typ == Viewtype::Text && msg.is_empty() && part.param.get(Param::Quote).is_none();
|
typ == Viewtype::Text && msg.is_empty() && part.param.get(Param::Quote).is_none();
|
||||||
|
|
||||||
|
if let Some(contact_id) = group_changes.added_removed_id {
|
||||||
|
param.set(Param::ContactAddedRemoved, contact_id.to_u32().to_string());
|
||||||
|
}
|
||||||
|
|
||||||
save_mime_modified |= mime_parser.is_mime_modified && !part_is_empty && !hidden;
|
save_mime_modified |= mime_parser.is_mime_modified && !part_is_empty && !hidden;
|
||||||
let save_mime_modified = save_mime_modified && parts.peek().is_none();
|
let save_mime_modified = save_mime_modified && parts.peek().is_none();
|
||||||
|
|
||||||
@@ -2334,10 +2339,12 @@ struct GroupChangesInfo {
|
|||||||
/// Optional: A better message that should replace the original system message.
|
/// Optional: A better message that should replace the original system message.
|
||||||
/// If this is an empty string, the original system message should be trashed.
|
/// If this is an empty string, the original system message should be trashed.
|
||||||
better_msg: Option<String>,
|
better_msg: Option<String>,
|
||||||
|
/// Added/removed contact `better_msg` refers to.
|
||||||
|
added_removed_id: Option<ContactId>,
|
||||||
/// If true, the user should not be notified about the group change.
|
/// If true, the user should not be notified about the group change.
|
||||||
silent: bool,
|
silent: bool,
|
||||||
/// A list of additional group changes messages that should be shown in the chat.
|
/// A list of additional group changes messages that should be shown in the chat.
|
||||||
extra_msgs: Vec<(String, SystemMessage)>,
|
extra_msgs: Vec<(String, SystemMessage, Option<ContactId>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply group member list, name, avatar and protection status changes from the MIME message.
|
/// Apply group member list, name, avatar and protection status changes from the MIME message.
|
||||||
@@ -2654,6 +2661,11 @@ async fn apply_group_changes(
|
|||||||
}
|
}
|
||||||
Ok(GroupChangesInfo {
|
Ok(GroupChangesInfo {
|
||||||
better_msg,
|
better_msg,
|
||||||
|
added_removed_id: if added_id.is_some() {
|
||||||
|
added_id
|
||||||
|
} else {
|
||||||
|
removed_id
|
||||||
|
},
|
||||||
silent,
|
silent,
|
||||||
extra_msgs: group_changes_msgs,
|
extra_msgs: group_changes_msgs,
|
||||||
})
|
})
|
||||||
@@ -2665,7 +2677,7 @@ async fn group_changes_msgs(
|
|||||||
added_ids: &HashSet<ContactId>,
|
added_ids: &HashSet<ContactId>,
|
||||||
removed_ids: &HashSet<ContactId>,
|
removed_ids: &HashSet<ContactId>,
|
||||||
chat_id: ChatId,
|
chat_id: ChatId,
|
||||||
) -> Result<Vec<(String, SystemMessage)>> {
|
) -> Result<Vec<(String, SystemMessage, Option<ContactId>)>> {
|
||||||
let mut group_changes_msgs = Vec::new();
|
let mut group_changes_msgs = Vec::new();
|
||||||
if !added_ids.is_empty() {
|
if !added_ids.is_empty() {
|
||||||
warn!(
|
warn!(
|
||||||
@@ -2686,6 +2698,7 @@ async fn group_changes_msgs(
|
|||||||
stock_str::msg_add_member_local(context, contact.get_addr(), ContactId::UNDEFINED)
|
stock_str::msg_add_member_local(context, contact.get_addr(), ContactId::UNDEFINED)
|
||||||
.await,
|
.await,
|
||||||
SystemMessage::MemberAddedToGroup,
|
SystemMessage::MemberAddedToGroup,
|
||||||
|
Some(contact.id),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
for contact_id in removed_ids {
|
for contact_id in removed_ids {
|
||||||
@@ -2694,6 +2707,7 @@ async fn group_changes_msgs(
|
|||||||
stock_str::msg_del_member_local(context, contact.get_addr(), ContactId::UNDEFINED)
|
stock_str::msg_del_member_local(context, contact.get_addr(), ContactId::UNDEFINED)
|
||||||
.await,
|
.await,
|
||||||
SystemMessage::MemberRemovedFromGroup,
|
SystemMessage::MemberRemovedFromGroup,
|
||||||
|
Some(contact.id),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -126,6 +126,7 @@ pub(super) async fn start_protocol(context: &Context, invite: QrInvite) -> Resul
|
|||||||
Some(ts_start),
|
Some(ts_start),
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
chat_id.spawn_securejoin_wait(context, constants::SECUREJOIN_WAIT_TIMEOUT);
|
chat_id.spawn_securejoin_wait(context, constants::SECUREJOIN_WAIT_TIMEOUT);
|
||||||
|
|||||||
@@ -439,6 +439,7 @@ pub(crate) async fn send_msg_to_smtp(
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -537,14 +537,6 @@ trait StockStringMods: AsRef<str> + Sized {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ContactId {
|
impl ContactId {
|
||||||
/// Get contact name and address for stock string, e.g. `Bob (bob@example.net)`
|
|
||||||
async fn get_stock_name_n_addr(self, context: &Context) -> String {
|
|
||||||
Contact::get_by_id(context, self)
|
|
||||||
.await
|
|
||||||
.map(|contact| contact.get_name_n_addr())
|
|
||||||
.unwrap_or_else(|_| self.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get contact name, e.g. `Bob`, or `bob@example.net` if no name is set.
|
/// Get contact name, e.g. `Bob`, or `bob@example.net` if no name is set.
|
||||||
async fn get_stock_name(self, context: &Context) -> String {
|
async fn get_stock_name(self, context: &Context) -> String {
|
||||||
Contact::get_by_id(context, self)
|
Contact::get_by_id(context, self)
|
||||||
@@ -613,7 +605,7 @@ pub(crate) async fn msg_grp_name(
|
|||||||
.await
|
.await
|
||||||
.replace1(from_group)
|
.replace1(from_group)
|
||||||
.replace2(to_group)
|
.replace2(to_group)
|
||||||
.replace3(&by_contact.get_stock_name_n_addr(context).await)
|
.replace3(&by_contact.get_stock_name(context).await)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -623,7 +615,7 @@ pub(crate) async fn msg_grp_img_changed(context: &Context, by_contact: ContactId
|
|||||||
} else {
|
} else {
|
||||||
translated(context, StockMessage::MsgGrpImgChangedBy)
|
translated(context, StockMessage::MsgGrpImgChangedBy)
|
||||||
.await
|
.await
|
||||||
.replace1(&by_contact.get_stock_name_n_addr(context).await)
|
.replace1(&by_contact.get_stock_name(context).await)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -637,7 +629,7 @@ pub(crate) async fn msg_add_member_remote(context: &Context, added_member_addr:
|
|||||||
let whom = &match Contact::lookup_id_by_addr(context, addr, Origin::Unknown).await {
|
let whom = &match Contact::lookup_id_by_addr(context, addr, Origin::Unknown).await {
|
||||||
Ok(Some(contact_id)) => Contact::get_by_id(context, contact_id)
|
Ok(Some(contact_id)) => Contact::get_by_id(context, contact_id)
|
||||||
.await
|
.await
|
||||||
.map(|contact| contact.get_authname_n_addr())
|
.map(|contact| contact.get_authname_or_addr())
|
||||||
.unwrap_or_else(|_| addr.to_string()),
|
.unwrap_or_else(|_| addr.to_string()),
|
||||||
_ => addr.to_string(),
|
_ => addr.to_string(),
|
||||||
};
|
};
|
||||||
@@ -659,7 +651,7 @@ pub(crate) async fn msg_add_member_local(
|
|||||||
let whom = &match Contact::lookup_id_by_addr(context, addr, Origin::Unknown).await {
|
let whom = &match Contact::lookup_id_by_addr(context, addr, Origin::Unknown).await {
|
||||||
Ok(Some(contact_id)) => Contact::get_by_id(context, contact_id)
|
Ok(Some(contact_id)) => Contact::get_by_id(context, contact_id)
|
||||||
.await
|
.await
|
||||||
.map(|contact| contact.get_name_n_addr())
|
.map(|contact| contact.get_display_name().to_string())
|
||||||
.unwrap_or_else(|_| addr.to_string()),
|
.unwrap_or_else(|_| addr.to_string()),
|
||||||
_ => addr.to_string(),
|
_ => addr.to_string(),
|
||||||
};
|
};
|
||||||
@@ -675,7 +667,7 @@ pub(crate) async fn msg_add_member_local(
|
|||||||
translated(context, StockMessage::MsgAddMemberBy)
|
translated(context, StockMessage::MsgAddMemberBy)
|
||||||
.await
|
.await
|
||||||
.replace1(whom)
|
.replace1(whom)
|
||||||
.replace2(&by_contact.get_stock_name_n_addr(context).await)
|
.replace2(&by_contact.get_stock_name(context).await)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -688,7 +680,7 @@ pub(crate) async fn msg_del_member_remote(context: &Context, removed_member_addr
|
|||||||
let whom = &match Contact::lookup_id_by_addr(context, addr, Origin::Unknown).await {
|
let whom = &match Contact::lookup_id_by_addr(context, addr, Origin::Unknown).await {
|
||||||
Ok(Some(contact_id)) => Contact::get_by_id(context, contact_id)
|
Ok(Some(contact_id)) => Contact::get_by_id(context, contact_id)
|
||||||
.await
|
.await
|
||||||
.map(|contact| contact.get_authname_n_addr())
|
.map(|contact| contact.get_authname_or_addr())
|
||||||
.unwrap_or_else(|_| addr.to_string()),
|
.unwrap_or_else(|_| addr.to_string()),
|
||||||
_ => addr.to_string(),
|
_ => addr.to_string(),
|
||||||
};
|
};
|
||||||
@@ -710,7 +702,7 @@ pub(crate) async fn msg_del_member_local(
|
|||||||
let whom = &match Contact::lookup_id_by_addr(context, addr, Origin::Unknown).await {
|
let whom = &match Contact::lookup_id_by_addr(context, addr, Origin::Unknown).await {
|
||||||
Ok(Some(contact_id)) => Contact::get_by_id(context, contact_id)
|
Ok(Some(contact_id)) => Contact::get_by_id(context, contact_id)
|
||||||
.await
|
.await
|
||||||
.map(|contact| contact.get_name_n_addr())
|
.map(|contact| contact.get_display_name().to_string())
|
||||||
.unwrap_or_else(|_| addr.to_string()),
|
.unwrap_or_else(|_| addr.to_string()),
|
||||||
_ => addr.to_string(),
|
_ => addr.to_string(),
|
||||||
};
|
};
|
||||||
@@ -726,7 +718,7 @@ pub(crate) async fn msg_del_member_local(
|
|||||||
translated(context, StockMessage::MsgDelMemberBy)
|
translated(context, StockMessage::MsgDelMemberBy)
|
||||||
.await
|
.await
|
||||||
.replace1(whom)
|
.replace1(whom)
|
||||||
.replace2(&by_contact.get_stock_name_n_addr(context).await)
|
.replace2(&by_contact.get_stock_name(context).await)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -742,7 +734,7 @@ pub(crate) async fn msg_group_left_local(context: &Context, by_contact: ContactI
|
|||||||
} else {
|
} else {
|
||||||
translated(context, StockMessage::MsgGroupLeftBy)
|
translated(context, StockMessage::MsgGroupLeftBy)
|
||||||
.await
|
.await
|
||||||
.replace1(&by_contact.get_stock_name_n_addr(context).await)
|
.replace1(&by_contact.get_stock_name(context).await)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -804,7 +796,7 @@ pub(crate) async fn msg_grp_img_deleted(context: &Context, by_contact: ContactId
|
|||||||
} else {
|
} else {
|
||||||
translated(context, StockMessage::MsgGrpImgDeletedBy)
|
translated(context, StockMessage::MsgGrpImgDeletedBy)
|
||||||
.await
|
.await
|
||||||
.replace1(&by_contact.get_stock_name_n_addr(context).await)
|
.replace1(&by_contact.get_stock_name(context).await)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -821,7 +813,7 @@ pub(crate) async fn secure_join_started(
|
|||||||
if let Ok(contact) = Contact::get_by_id(context, inviter_contact_id).await {
|
if let Ok(contact) = Contact::get_by_id(context, inviter_contact_id).await {
|
||||||
translated(context, StockMessage::SecureJoinStarted)
|
translated(context, StockMessage::SecureJoinStarted)
|
||||||
.await
|
.await
|
||||||
.replace1(&contact.get_name_n_addr())
|
.replace1(contact.get_display_name())
|
||||||
.replace2(contact.get_display_name())
|
.replace2(contact.get_display_name())
|
||||||
} else {
|
} else {
|
||||||
format!("secure_join_started: unknown contact {inviter_contact_id}")
|
format!("secure_join_started: unknown contact {inviter_contact_id}")
|
||||||
@@ -871,7 +863,7 @@ pub(crate) async fn secure_join_group_qr_description(context: &Context, chat: &C
|
|||||||
/// Stock string: `%1$s verified.`.
|
/// Stock string: `%1$s verified.`.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) async fn contact_verified(context: &Context, contact: &Contact) -> String {
|
pub(crate) async fn contact_verified(context: &Context, contact: &Contact) -> String {
|
||||||
let addr = &contact.get_name_n_addr();
|
let addr = contact.get_display_name();
|
||||||
translated(context, StockMessage::ContactVerified)
|
translated(context, StockMessage::ContactVerified)
|
||||||
.await
|
.await
|
||||||
.replace1(addr)
|
.replace1(addr)
|
||||||
@@ -879,7 +871,7 @@ pub(crate) async fn contact_verified(context: &Context, contact: &Contact) -> St
|
|||||||
|
|
||||||
/// Stock string: `Cannot establish guaranteed end-to-end encryption with %1$s`.
|
/// Stock string: `Cannot establish guaranteed end-to-end encryption with %1$s`.
|
||||||
pub(crate) async fn contact_not_verified(context: &Context, contact: &Contact) -> String {
|
pub(crate) async fn contact_not_verified(context: &Context, contact: &Contact) -> String {
|
||||||
let addr = &contact.get_name_n_addr();
|
let addr = contact.get_display_name();
|
||||||
translated(context, StockMessage::ContactNotVerified)
|
translated(context, StockMessage::ContactNotVerified)
|
||||||
.await
|
.await
|
||||||
.replace1(addr)
|
.replace1(addr)
|
||||||
@@ -936,7 +928,7 @@ pub(crate) async fn msg_location_enabled_by(context: &Context, contact: ContactI
|
|||||||
} else {
|
} else {
|
||||||
translated(context, StockMessage::MsgLocationEnabledBy)
|
translated(context, StockMessage::MsgLocationEnabledBy)
|
||||||
.await
|
.await
|
||||||
.replace1(&contact.get_stock_name_n_addr(context).await)
|
.replace1(&contact.get_stock_name(context).await)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -998,7 +990,7 @@ pub(crate) async fn msg_ephemeral_timer_disabled(
|
|||||||
} else {
|
} else {
|
||||||
translated(context, StockMessage::MsgEphemeralTimerDisabledBy)
|
translated(context, StockMessage::MsgEphemeralTimerDisabledBy)
|
||||||
.await
|
.await
|
||||||
.replace1(&by_contact.get_stock_name_n_addr(context).await)
|
.replace1(&by_contact.get_stock_name(context).await)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1016,7 +1008,7 @@ pub(crate) async fn msg_ephemeral_timer_enabled(
|
|||||||
translated(context, StockMessage::MsgEphemeralTimerEnabledBy)
|
translated(context, StockMessage::MsgEphemeralTimerEnabledBy)
|
||||||
.await
|
.await
|
||||||
.replace1(timer)
|
.replace1(timer)
|
||||||
.replace2(&by_contact.get_stock_name_n_addr(context).await)
|
.replace2(&by_contact.get_stock_name(context).await)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1027,7 +1019,7 @@ pub(crate) async fn msg_ephemeral_timer_minute(context: &Context, by_contact: Co
|
|||||||
} else {
|
} else {
|
||||||
translated(context, StockMessage::MsgEphemeralTimerMinuteBy)
|
translated(context, StockMessage::MsgEphemeralTimerMinuteBy)
|
||||||
.await
|
.await
|
||||||
.replace1(&by_contact.get_stock_name_n_addr(context).await)
|
.replace1(&by_contact.get_stock_name(context).await)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1038,7 +1030,7 @@ pub(crate) async fn msg_ephemeral_timer_hour(context: &Context, by_contact: Cont
|
|||||||
} else {
|
} else {
|
||||||
translated(context, StockMessage::MsgEphemeralTimerHourBy)
|
translated(context, StockMessage::MsgEphemeralTimerHourBy)
|
||||||
.await
|
.await
|
||||||
.replace1(&by_contact.get_stock_name_n_addr(context).await)
|
.replace1(&by_contact.get_stock_name(context).await)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1049,7 +1041,7 @@ pub(crate) async fn msg_ephemeral_timer_day(context: &Context, by_contact: Conta
|
|||||||
} else {
|
} else {
|
||||||
translated(context, StockMessage::MsgEphemeralTimerDayBy)
|
translated(context, StockMessage::MsgEphemeralTimerDayBy)
|
||||||
.await
|
.await
|
||||||
.replace1(&by_contact.get_stock_name_n_addr(context).await)
|
.replace1(&by_contact.get_stock_name(context).await)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1060,7 +1052,7 @@ pub(crate) async fn msg_ephemeral_timer_week(context: &Context, by_contact: Cont
|
|||||||
} else {
|
} else {
|
||||||
translated(context, StockMessage::MsgEphemeralTimerWeekBy)
|
translated(context, StockMessage::MsgEphemeralTimerWeekBy)
|
||||||
.await
|
.await
|
||||||
.replace1(&by_contact.get_stock_name_n_addr(context).await)
|
.replace1(&by_contact.get_stock_name(context).await)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1142,7 +1134,7 @@ pub(crate) async fn msg_ephemeral_timer_minutes(
|
|||||||
translated(context, StockMessage::MsgEphemeralTimerMinutesBy)
|
translated(context, StockMessage::MsgEphemeralTimerMinutesBy)
|
||||||
.await
|
.await
|
||||||
.replace1(minutes)
|
.replace1(minutes)
|
||||||
.replace2(&by_contact.get_stock_name_n_addr(context).await)
|
.replace2(&by_contact.get_stock_name(context).await)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1160,7 +1152,7 @@ pub(crate) async fn msg_ephemeral_timer_hours(
|
|||||||
translated(context, StockMessage::MsgEphemeralTimerHoursBy)
|
translated(context, StockMessage::MsgEphemeralTimerHoursBy)
|
||||||
.await
|
.await
|
||||||
.replace1(hours)
|
.replace1(hours)
|
||||||
.replace2(&by_contact.get_stock_name_n_addr(context).await)
|
.replace2(&by_contact.get_stock_name(context).await)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1178,7 +1170,7 @@ pub(crate) async fn msg_ephemeral_timer_days(
|
|||||||
translated(context, StockMessage::MsgEphemeralTimerDaysBy)
|
translated(context, StockMessage::MsgEphemeralTimerDaysBy)
|
||||||
.await
|
.await
|
||||||
.replace1(days)
|
.replace1(days)
|
||||||
.replace2(&by_contact.get_stock_name_n_addr(context).await)
|
.replace2(&by_contact.get_stock_name(context).await)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1196,7 +1188,7 @@ pub(crate) async fn msg_ephemeral_timer_weeks(
|
|||||||
translated(context, StockMessage::MsgEphemeralTimerWeeksBy)
|
translated(context, StockMessage::MsgEphemeralTimerWeeksBy)
|
||||||
.await
|
.await
|
||||||
.replace1(weeks)
|
.replace1(weeks)
|
||||||
.replace2(&by_contact.get_stock_name_n_addr(context).await)
|
.replace2(&by_contact.get_stock_name(context).await)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,10 +54,7 @@ async fn test_stock_string_repl_str() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
let contact = Contact::get_by_id(&t.ctx, contact_id).await.unwrap();
|
let contact = Contact::get_by_id(&t.ctx, contact_id).await.unwrap();
|
||||||
// uses %1$s substitution
|
// uses %1$s substitution
|
||||||
assert_eq!(
|
assert_eq!(contact_verified(&t, &contact).await, "Someone verified.");
|
||||||
contact_verified(&t, &contact).await,
|
|
||||||
"Someone (someone@example.org) verified."
|
|
||||||
);
|
|
||||||
// We have no string using %1$d to test...
|
// We have no string using %1$d to test...
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +92,7 @@ async fn test_stock_system_msg_add_member_by_me_with_displayname() {
|
|||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
msg_add_member_local(&t, "alice@example.org", ContactId::SELF).await,
|
msg_add_member_local(&t, "alice@example.org", ContactId::SELF).await,
|
||||||
"You added member Alice (alice@example.org)."
|
"You added member Alice."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +109,7 @@ async fn test_stock_system_msg_add_member_by_other_with_displayname() {
|
|||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
msg_add_member_local(&t, "alice@example.org", contact_id,).await,
|
msg_add_member_local(&t, "alice@example.org", contact_id,).await,
|
||||||
"Member Alice (alice@example.org) added by Bob (bob@example.com)."
|
"Member Alice added by Bob."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -743,10 +743,7 @@ mod tests {
|
|||||||
let fiona = &tcm.fiona().await;
|
let fiona = &tcm.fiona().await;
|
||||||
tcm.exec_securejoin_qr(fiona, alice2, &qr).await;
|
tcm.exec_securejoin_qr(fiona, alice2, &qr).await;
|
||||||
let msg = fiona.get_last_msg().await;
|
let msg = fiona.get_last_msg().await;
|
||||||
assert_eq!(
|
assert_eq!(msg.text, "Member Me added by alice@example.org.");
|
||||||
msg.text,
|
|
||||||
"Member Me (fiona@example.net) added by alice@example.org."
|
|
||||||
);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -388,6 +388,7 @@ impl Context {
|
|||||||
None,
|
None,
|
||||||
Some(&instance),
|
Some(&instance),
|
||||||
Some(from_id),
|
Some(from_id),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,5 +5,5 @@ Msg#11: info (Contact#Contact#Info): alice@example.org invited you to join this
|
|||||||
Waiting for the device of alice@example.org to reply… [NOTICED][INFO]
|
Waiting for the device of alice@example.org to reply… [NOTICED][INFO]
|
||||||
Msg#13: info (Contact#Contact#Info): alice@example.org replied, waiting for being added to the group… [NOTICED][INFO]
|
Msg#13: info (Contact#Contact#Info): alice@example.org replied, waiting for being added to the group… [NOTICED][INFO]
|
||||||
Msg#17: info (Contact#Contact#Info): Messages are guaranteed to be end-to-end encrypted from now on. [NOTICED][INFO 🛡️]
|
Msg#17: info (Contact#Contact#Info): Messages are guaranteed to be end-to-end encrypted from now on. [NOTICED][INFO 🛡️]
|
||||||
Msg#18🔒: (Contact#Contact#10): Member Me (fiona@example.net) added by alice@example.org. [FRESH][INFO]
|
Msg#18🔒: (Contact#Contact#10): Member Me added by alice@example.org. [FRESH][INFO]
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user