mirror of
https://github.com/chatmail/core.git
synced 2026-06-27 18:16:36 +03:00
Compare commits
16 Commits
1.67.0
...
chatlist_c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e35c23f29 | ||
|
|
7f97768c56 | ||
|
|
ecd548a7aa | ||
|
|
62efb0795b | ||
|
|
53f042ee08 | ||
|
|
6ce97bd0cd | ||
|
|
c29149e74c | ||
|
|
487f7593ce | ||
|
|
6b3b33d2a0 | ||
|
|
2d70ccc2bf | ||
|
|
e90fc9504a | ||
|
|
5108314c03 | ||
|
|
f2b86a1c0f | ||
|
|
9583d41446 | ||
|
|
e594064f93 | ||
|
|
eb610c27bf |
15
CHANGELOG.md
15
CHANGELOG.md
@@ -1,5 +1,20 @@
|
||||
# Changelog
|
||||
|
||||
## Unreleased
|
||||
|
||||
### API Changes
|
||||
- add `DC_EVENT_CHAT_LIST_CHANGED` event
|
||||
|
||||
### Changes
|
||||
- add chatlist changed event
|
||||
|
||||
## 1.68.0
|
||||
|
||||
### Fixes
|
||||
- fix chat assignment when forwarding #2843
|
||||
- fix layout issues with the generated QR code svg #2842
|
||||
|
||||
|
||||
## 1.67.0
|
||||
|
||||
### API changes
|
||||
|
||||
12
Cargo.lock
generated
12
Cargo.lock
generated
@@ -114,9 +114,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.47"
|
||||
version = "1.0.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38d9ff5d688f1c13395289f67db01d4826b46dd694e7580accdc3e8430f2d98e"
|
||||
checksum = "62e1f47f7dc0422027a4e370dd4548d4d66b26782e513e98dca1e689e058a80e"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
@@ -1056,7 +1056,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat"
|
||||
version = "1.67.0"
|
||||
version = "1.68.0"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"anyhow",
|
||||
@@ -1136,7 +1136,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat_ffi"
|
||||
version = "1.67.0"
|
||||
version = "1.68.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-std",
|
||||
@@ -2067,9 +2067,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.107"
|
||||
version = "0.2.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219"
|
||||
checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat"
|
||||
version = "1.67.0"
|
||||
version = "1.68.0"
|
||||
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
|
||||
edition = "2018"
|
||||
license = "MPL-2.0"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat_ffi"
|
||||
version = "1.67.0"
|
||||
version = "1.68.0"
|
||||
description = "Deltachat FFI"
|
||||
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
|
||||
edition = "2018"
|
||||
|
||||
@@ -5301,6 +5301,14 @@ void dc_event_unref(dc_event_t* event);
|
||||
*/
|
||||
#define DC_EVENT_CHAT_EPHEMERAL_TIMER_MODIFIED 2021
|
||||
|
||||
/**
|
||||
* Chatlist changed and ui should reload it (reordering, entry added or removed)
|
||||
*
|
||||
* This event is fired when the ordering or contents of the chatlist change,
|
||||
* for changes in individual chat list items listen to `IncomingMsg`, `MsgsNoticed`, `MsgDelivered`, `MsgFailed`, `MsgRead` and `ChatModified`
|
||||
*/
|
||||
#define DC_EVENT_CHAT_LIST_CHANGED 2025
|
||||
|
||||
|
||||
/**
|
||||
* Contact(s) created, renamed, verified, blocked or deleted.
|
||||
|
||||
@@ -440,6 +440,7 @@ pub unsafe extern "C" fn dc_event_get_data1_int(event: *mut dc_event_t) -> libc:
|
||||
| EventType::Error(_)
|
||||
| EventType::ConnectivityChanged
|
||||
| EventType::SelfavatarChanged
|
||||
| EventType::ChatListChanged
|
||||
| EventType::ErrorSelfNotInGroup(_) => 0,
|
||||
EventType::MsgsChanged { chat_id, .. }
|
||||
| EventType::IncomingMsg { chat_id, .. }
|
||||
@@ -491,7 +492,8 @@ pub unsafe extern "C" fn dc_event_get_data2_int(event: *mut dc_event_t) -> libc:
|
||||
| EventType::MsgsNoticed(_)
|
||||
| EventType::ConnectivityChanged
|
||||
| EventType::SelfavatarChanged
|
||||
| EventType::ChatModified(_) => 0,
|
||||
| EventType::ChatModified(_)
|
||||
| EventType::ChatListChanged => 0,
|
||||
EventType::MsgsChanged { msg_id, .. }
|
||||
| EventType::IncomingMsg { msg_id, .. }
|
||||
| EventType::MsgDelivered { msg_id, .. }
|
||||
@@ -534,6 +536,7 @@ pub unsafe extern "C" fn dc_event_get_data2_str(event: *mut dc_event_t) -> *mut
|
||||
| EventType::MsgFailed { .. }
|
||||
| EventType::MsgRead { .. }
|
||||
| EventType::ChatModified(_)
|
||||
| EventType::ChatListChanged
|
||||
| EventType::ContactsChanged(_)
|
||||
| EventType::LocationChanged(_)
|
||||
| EventType::ImexProgress(_)
|
||||
|
||||
33
draft/aeap-mvp.rst
Normal file
33
draft/aeap-mvp.rst
Normal file
@@ -0,0 +1,33 @@
|
||||
AEAP MVP
|
||||
========
|
||||
|
||||
Changes to the UIs
|
||||
------------------
|
||||
|
||||
- The secondary self addresses (see below) are shown in the UI, but not editable.
|
||||
|
||||
- When the user changed the email address in the configure screen, show a dialog to the user, either directly explaining things or with a link to the FAQ (see "Other" below)
|
||||
|
||||
Changes in the core
|
||||
-------------------
|
||||
|
||||
- We have one primary self address and any number of secondary self addresses. `is_self_addr()` checks all of them.
|
||||
|
||||
- If the user does a reconfigure and changes the email address, the previous address is added as a secondary self address.
|
||||
|
||||
- don't forget to deduplicate secondary self addresses in case the user switches back and forth between addresses).
|
||||
|
||||
- The key stays the same.
|
||||
|
||||
- No changes for 1:1 chats, there simply is a new one
|
||||
|
||||
- When we send a message to a group, and the primary address is not a member of a group, but a secondary address is:
|
||||
|
||||
Add Chat-Group-Member-Removed=<old address> and Chat-Group-Member-Added=<new address> headers to this message
|
||||
|
||||
- On the receiving side, make sure that we accept this (even in verified groups) if the message is signed and the key stayed the same
|
||||
|
||||
Other
|
||||
-----
|
||||
|
||||
- The user is responsible that messages to the old address arrive at the new address, for example by configuring the old provider to forward all emails to the new one.
|
||||
@@ -96,6 +96,7 @@ async fn reset_tables(context: &Context, bits: i32) {
|
||||
chat_id: ChatId::new(0),
|
||||
msg_id: MsgId::new(0),
|
||||
});
|
||||
context.emit_event(EventType::ChatListChanged);
|
||||
}
|
||||
|
||||
async fn poke_eml_file(context: &Context, filename: impl AsRef<Path>) -> Result<()> {
|
||||
@@ -167,6 +168,7 @@ async fn poke_spec(context: &Context, spec: Option<&str>) -> bool {
|
||||
chat_id: ChatId::new(0),
|
||||
msg_id: MsgId::new(0),
|
||||
});
|
||||
context.emit_event(EventType::ChatListChanged);
|
||||
}
|
||||
true
|
||||
}
|
||||
@@ -755,7 +757,12 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
|
||||
"groupname" => {
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
ensure!(!arg1.is_empty(), "Argument <name> missing.");
|
||||
chat::set_chat_name(&context, sel_chat.as_ref().unwrap().get_id(), arg1).await?;
|
||||
chat::set_chat_name(
|
||||
&context,
|
||||
sel_chat.as_ref().unwrap().get_id(),
|
||||
&format!("{} {}", arg1, arg2).trim(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
println!("Chat name set");
|
||||
}
|
||||
|
||||
@@ -114,6 +114,12 @@ fn receive_event(event: EventType) {
|
||||
yellow.paint(format!("Received CHAT_MODIFIED({})", chat))
|
||||
);
|
||||
}
|
||||
EventType::ChatListChanged(chat) => {
|
||||
info!(
|
||||
"{}",
|
||||
yellow.paint("Received CHAT_LIST_CHANGED")
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
info!("Received {:?}", event);
|
||||
}
|
||||
|
||||
104
src/chat.rs
104
src/chat.rs
@@ -211,6 +211,7 @@ impl ChatId {
|
||||
chat_id: ChatId::new(0),
|
||||
msg_id: MsgId::new(0),
|
||||
});
|
||||
context.emit_event(EventType::ChatListChanged);
|
||||
Ok(chat_id)
|
||||
}
|
||||
|
||||
@@ -301,10 +302,12 @@ impl ChatId {
|
||||
Chattype::Group => {
|
||||
info!(context, "Can't block groups yet, deleting the chat");
|
||||
self.delete(context).await?;
|
||||
context.emit_event(EventType::ChatListChanged);
|
||||
}
|
||||
Chattype::Mailinglist => {
|
||||
if self.set_blocked(context, Blocked::Yes).await? {
|
||||
context.emit_event(EventType::ChatModified(self));
|
||||
context.emit_event(EventType::ChatListChanged);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -345,6 +348,7 @@ impl ChatId {
|
||||
|
||||
if self.set_blocked(context, Blocked::Not).await? {
|
||||
context.emit_event(EventType::ChatModified(self));
|
||||
context.emit_event(EventType::ChatListChanged);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -489,6 +493,7 @@ impl ChatId {
|
||||
msg_id: MsgId::new(0),
|
||||
chat_id: ChatId::new(0),
|
||||
});
|
||||
context.emit_event(EventType::ChatListChanged);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -546,6 +551,7 @@ impl ChatId {
|
||||
msg_id: MsgId::new(0),
|
||||
chat_id: ChatId::new(0),
|
||||
});
|
||||
context.emit_event(EventType::ChatListChanged);
|
||||
|
||||
job::kill_action(context, Action::Housekeeping).await?;
|
||||
let j = job::Job::new(Action::Housekeeping, 0, Params::new(), 10);
|
||||
@@ -1992,7 +1998,8 @@ pub async fn get_chat_msgs(
|
||||
context.emit_event(EventType::MsgsChanged {
|
||||
msg_id: MsgId::new(0),
|
||||
chat_id: ChatId::new(0),
|
||||
})
|
||||
});
|
||||
context.emit_event(EventType::ChatListChanged);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2292,6 +2299,7 @@ pub async fn create_group_chat(
|
||||
msg_id: MsgId::new(0),
|
||||
chat_id: ChatId::new(0),
|
||||
});
|
||||
context.emit_event(EventType::ChatListChanged);
|
||||
|
||||
if protect == ProtectionStatus::Protected {
|
||||
// this part is to stay compatible to verified groups,
|
||||
@@ -2349,6 +2357,7 @@ pub async fn create_broadcast_list(context: &Context) -> Result<ChatId> {
|
||||
msg_id: MsgId::new(0),
|
||||
chat_id: ChatId::new(0),
|
||||
});
|
||||
context.emit_event(EventType::ChatListChanged);
|
||||
Ok(chat_id)
|
||||
}
|
||||
|
||||
@@ -2846,6 +2855,7 @@ pub async fn forward_msgs(context: &Context, msg_ids: &[MsgId], chat_id: ChatId)
|
||||
msg.param.remove(Param::ForcePlaintext);
|
||||
msg.param.remove(Param::Cmd);
|
||||
msg.param.remove(Param::OverrideSenderDisplayname);
|
||||
msg.in_reply_to = None;
|
||||
|
||||
// do not leak data as group names; a default subject is generated by mimfactory
|
||||
msg.subject = "".to_string();
|
||||
@@ -4291,6 +4301,98 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_forward_quote() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
let alice_chat = alice.create_chat(&bob).await;
|
||||
let bob_chat = bob.create_chat(&alice).await;
|
||||
|
||||
// Alice sends a message to Bob.
|
||||
let sent_msg = alice.send_text(alice_chat.id, "Hi Bob").await;
|
||||
bob.recv_msg(&sent_msg).await;
|
||||
let received_msg = bob.get_last_msg().await;
|
||||
|
||||
// Bob quotes received message and sends a reply to Alice.
|
||||
let mut reply = Message::new(Viewtype::Text);
|
||||
reply.set_text(Some("Reply".to_owned()));
|
||||
reply.set_quote(&bob, &received_msg).await?;
|
||||
let sent_reply = bob.send_msg(bob_chat.id, &mut reply).await;
|
||||
alice.recv_msg(&sent_reply).await;
|
||||
let received_reply = alice.get_last_msg().await;
|
||||
|
||||
// Alice forwards a reply.
|
||||
forward_msgs(&alice, &[received_reply.id], alice_chat.get_id()).await?;
|
||||
let forwarded_msg = alice.pop_sent_msg().await;
|
||||
bob.recv_msg(&forwarded_msg).await;
|
||||
|
||||
let alice_forwarded_msg = alice.get_last_msg().await;
|
||||
assert!(alice_forwarded_msg.quoted_message(&alice).await?.is_none());
|
||||
assert_eq!(
|
||||
alice_forwarded_msg.quoted_text(),
|
||||
Some("Hi Bob".to_string())
|
||||
);
|
||||
|
||||
let bob_forwarded_msg = bob.get_last_msg().await;
|
||||
assert!(bob_forwarded_msg.quoted_message(&bob).await?.is_none());
|
||||
assert_eq!(bob_forwarded_msg.quoted_text(), Some("Hi Bob".to_string()));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_forward_group() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
|
||||
let alice_chat = alice.create_chat(&bob).await;
|
||||
let bob_chat = bob.create_chat(&alice).await;
|
||||
|
||||
// Alice creates a group with Bob.
|
||||
let alice_group_chat_id =
|
||||
create_group_chat(&alice, ProtectionStatus::Unprotected, "Group").await?;
|
||||
let bob_id = Contact::create(&alice, "Bob", "bob@example.net").await?;
|
||||
let claire_id = Contact::create(&alice, "Claire", "claire@example.net").await?;
|
||||
add_contact_to_chat(&alice, alice_group_chat_id, bob_id).await?;
|
||||
add_contact_to_chat(&alice, alice_group_chat_id, claire_id).await?;
|
||||
let sent_group_msg = alice
|
||||
.send_text(alice_group_chat_id, "Hi Bob and Claire")
|
||||
.await;
|
||||
bob.recv_msg(&sent_group_msg).await;
|
||||
let bob_group_chat_id = bob.get_last_msg().await.chat_id;
|
||||
|
||||
// Alice deletes a message on her device.
|
||||
// This is needed to make assignment of further messages received in this group
|
||||
// based on `References:` header harder.
|
||||
// Previously this exposed a bug, so this is a regression test.
|
||||
message::delete_msgs(&alice, &[sent_group_msg.sender_msg_id]).await?;
|
||||
|
||||
// Alice sends a message to Bob.
|
||||
let sent_msg = alice.send_text(alice_chat.id, "Hi Bob").await;
|
||||
bob.recv_msg(&sent_msg).await;
|
||||
let received_msg = bob.get_last_msg().await;
|
||||
assert_eq!(received_msg.get_text(), Some("Hi Bob".to_string()));
|
||||
assert_eq!(received_msg.chat_id, bob_chat.id);
|
||||
|
||||
// Alice sends another message to Bob, this has first message as a parent.
|
||||
let sent_msg = alice.send_text(alice_chat.id, "Hello Bob").await;
|
||||
bob.recv_msg(&sent_msg).await;
|
||||
let received_msg = bob.get_last_msg().await;
|
||||
assert_eq!(received_msg.get_text(), Some("Hello Bob".to_string()));
|
||||
assert_eq!(received_msg.chat_id, bob_chat.id);
|
||||
|
||||
// Bob forwards message to a group chat with Alice.
|
||||
forward_msgs(&bob, &[received_msg.id], bob_group_chat_id).await?;
|
||||
let forwarded_msg = bob.pop_sent_msg().await;
|
||||
alice.recv_msg(&forwarded_msg).await;
|
||||
|
||||
let received_forwarded_msg = alice.get_last_msg_in(alice_group_chat_id).await;
|
||||
assert!(received_forwarded_msg.is_forwarded());
|
||||
assert_eq!(received_forwarded_msg.chat_id, alice_group_chat_id);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_only_minimal_data_are_forwarded() -> Result<()> {
|
||||
// send a message from Alice to a group with Bob
|
||||
|
||||
@@ -314,6 +314,7 @@ impl Context {
|
||||
msg_id: MsgId::new(0),
|
||||
chat_id: ChatId::new(0),
|
||||
});
|
||||
self.emit_event(EventType::ChatListChanged);
|
||||
ret
|
||||
}
|
||||
Config::Displayname => {
|
||||
|
||||
@@ -252,12 +252,16 @@ impl Contact {
|
||||
|
||||
/// Block the given contact.
|
||||
pub async fn block(context: &Context, id: u32) -> Result<()> {
|
||||
set_block_contact(context, id, true).await
|
||||
set_block_contact(context, id, true).await?;
|
||||
context.emit_event(EventType::ChatListChanged);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Unblock the given contact.
|
||||
pub async fn unblock(context: &Context, id: u32) -> Result<()> {
|
||||
set_block_contact(context, id, false).await
|
||||
set_block_contact(context, id, false).await?;
|
||||
context.emit_event(EventType::ChatListChanged);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add a single contact as a result of an _explicit_ user action.
|
||||
|
||||
@@ -2113,6 +2113,8 @@ fn set_better_msg(mime_parser: &mut MimeMessage, better_msg: impl AsRef<str>) {
|
||||
/// Returns the last message referenced from `References` header if it is in the database.
|
||||
///
|
||||
/// For Delta Chat messages it is the last message in the chat of the sender.
|
||||
///
|
||||
/// Note that the returned message may be trashed.
|
||||
async fn get_previous_message(
|
||||
context: &Context,
|
||||
mime_parser: &MimeMessage,
|
||||
@@ -2128,6 +2130,8 @@ async fn get_previous_message(
|
||||
}
|
||||
|
||||
/// Given a list of Message-IDs, returns the latest message found in the database.
|
||||
///
|
||||
/// Only messages that are not in the trash chat are considered.
|
||||
async fn get_rfc724_mid_in_list(context: &Context, mid_list: &str) -> Result<Option<Message>> {
|
||||
if mid_list.is_empty() {
|
||||
return Ok(None);
|
||||
@@ -2135,7 +2139,10 @@ async fn get_rfc724_mid_in_list(context: &Context, mid_list: &str) -> Result<Opt
|
||||
|
||||
for id in parse_message_ids(mid_list).iter().rev() {
|
||||
if let Some((_, _, msg_id)) = rfc724_mid_exists(context, id).await? {
|
||||
return Ok(Some(Message::load_from_db(context, msg_id).await?));
|
||||
let msg = Message::load_from_db(context, msg_id).await?;
|
||||
if msg.chat_id != DC_CHAT_ID_TRASH {
|
||||
return Ok(Some(msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4741,4 +4748,66 @@ Second thread."#;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_get_parent_message() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
|
||||
let mime = br#"Subject: First
|
||||
Message-ID: first@example.net
|
||||
To: Alice <alice@example.com>
|
||||
From: Bob <bob@example.net>
|
||||
Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no
|
||||
|
||||
First."#;
|
||||
dc_receive_imf(&t, mime, "INBOX", 1, false).await?;
|
||||
let first = t.get_last_msg().await;
|
||||
let mime = br#"Subject: Second
|
||||
Message-ID: second@example.net
|
||||
To: Alice <alice@example.com>
|
||||
From: Bob <bob@example.net>
|
||||
Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no
|
||||
|
||||
First."#;
|
||||
dc_receive_imf(&t, mime, "INBOX", 2, false).await?;
|
||||
let second = t.get_last_msg().await;
|
||||
let mime = br#"Subject: Third
|
||||
Message-ID: third@example.net
|
||||
To: Alice <alice@example.com>
|
||||
From: Bob <bob@example.net>
|
||||
Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no
|
||||
|
||||
First."#;
|
||||
dc_receive_imf(&t, mime, "INBOX", 3, false).await?;
|
||||
let third = t.get_last_msg().await;
|
||||
|
||||
let mime = br#"Subject: Message with references.
|
||||
Message-ID: second@example.net
|
||||
To: Alice <alice@example.com>
|
||||
From: Bob <bob@example.net>
|
||||
In-Reply-To: <third@example.net>
|
||||
References: <second@example.net> <nonexistent@example.net> <first@example.net>
|
||||
Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no
|
||||
|
||||
Message with references."#;
|
||||
let mime_parser = MimeMessage::from_bytes(&t, &mime[..]).await?;
|
||||
|
||||
let parent = get_parent_message(&t, &mime_parser).await?.unwrap();
|
||||
assert_eq!(parent.id, first.id);
|
||||
|
||||
message::delete_msgs(&t, &[first.id]).await?;
|
||||
let parent = get_parent_message(&t, &mime_parser).await?.unwrap();
|
||||
assert_eq!(parent.id, second.id);
|
||||
|
||||
message::delete_msgs(&t, &[second.id]).await?;
|
||||
let parent = get_parent_message(&t, &mime_parser).await?.unwrap();
|
||||
assert_eq!(parent.id, third.id);
|
||||
|
||||
message::delete_msgs(&t, &[third.id]).await?;
|
||||
let parent = get_parent_message(&t, &mime_parser).await?;
|
||||
assert!(parent.is_none());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -422,6 +422,7 @@ pub async fn schedule_ephemeral_task(context: &Context) {
|
||||
chat_id: ChatId::new(0),
|
||||
msg_id: MsgId::new(0),
|
||||
});
|
||||
context1.emit_event(EventType::ChatListChanged);
|
||||
});
|
||||
*context.ephemeral_task.write().await = Some(ephemeral_task);
|
||||
} else {
|
||||
@@ -430,6 +431,7 @@ pub async fn schedule_ephemeral_task(context: &Context) {
|
||||
chat_id: ChatId::new(0),
|
||||
msg_id: MsgId::new(0),
|
||||
});
|
||||
context.emit_event(EventType::ChatListChanged);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,6 +248,13 @@ pub enum EventType {
|
||||
timer: EphemeralTimer,
|
||||
},
|
||||
|
||||
// Chatlist changed and ui should reload it (reordering, entry added or removed)
|
||||
//
|
||||
// This event is fired when the ordering or contents of the chatlist change,
|
||||
// for changes in individual chat list items listen to `IncomingMsg`, `MsgsNoticed`, `MsgDelivered`, `MsgFailed`, `MsgRead` and `ChatModified`
|
||||
#[strum(props(id = "2025"))]
|
||||
ChatListChanged,
|
||||
|
||||
/// Contact(s) created, renamed, blocked or deleted.
|
||||
///
|
||||
/// @param data1 (int) If set, this is the contact_id of an added contact that should be selected.
|
||||
|
||||
@@ -877,7 +877,7 @@ impl Message {
|
||||
}
|
||||
|
||||
pub async fn quoted_message(&self, context: &Context) -> Result<Option<Message>> {
|
||||
if self.param.get(Param::Quote).is_some() {
|
||||
if self.param.get(Param::Quote).is_some() && !self.is_forwarded() {
|
||||
if let Some(in_reply_to) = &self.in_reply_to {
|
||||
if let Some((_, _, msg_id)) = rfc724_mid_exists(context, in_reply_to).await? {
|
||||
let msg = Message::load_from_db(context, msg_id).await?;
|
||||
@@ -1295,6 +1295,7 @@ pub async fn delete_msgs(context: &Context, msg_ids: &[MsgId]) -> Result<()> {
|
||||
chat_id: ChatId::new(0),
|
||||
msg_id: MsgId::new(0),
|
||||
});
|
||||
context.emit_event(EventType::ChatListChanged);
|
||||
job::kill_action(context, Action::Housekeeping).await?;
|
||||
job::add(
|
||||
context,
|
||||
|
||||
@@ -80,11 +80,6 @@ fn inner_generate_secure_join_qr_code(
|
||||
let qr_code_size = 400.0;
|
||||
let qr_translate_up = 40.0;
|
||||
let text_y_pos = ((height - qr_code_size) / 2.0) + qr_code_size;
|
||||
let (text_font_size, max_text_width) = if qrcode_description.len() <= 75 {
|
||||
(27.0, 32)
|
||||
} else {
|
||||
(19.0, 38)
|
||||
};
|
||||
let avatar_border_size = 9.0;
|
||||
let card_border_size = 2.0;
|
||||
let card_roundness = 40.0;
|
||||
@@ -142,13 +137,25 @@ fn inner_generate_secure_join_qr_code(
|
||||
.attr("transform", format!("scale({})", scale));
|
||||
});
|
||||
});
|
||||
|
||||
// Text
|
||||
for (count, line) in textwrap::fill(qrcode_description, max_text_width)
|
||||
.split('\n')
|
||||
.enumerate()
|
||||
const BIG_TEXT_CHARS_PER_LINE: usize = 32;
|
||||
const SMALL_TEXT_CHARS_PER_LINE: usize = 38;
|
||||
let chars_per_line = if qrcode_description.len() > SMALL_TEXT_CHARS_PER_LINE*2 {
|
||||
SMALL_TEXT_CHARS_PER_LINE
|
||||
} else {
|
||||
BIG_TEXT_CHARS_PER_LINE
|
||||
};
|
||||
let lines = textwrap::fill(qrcode_description, chars_per_line);
|
||||
let (text_font_size, text_y_shift) = if lines.split('\n').count() <= 2 {
|
||||
(27.0, 0.0)
|
||||
} else {
|
||||
(19.0, -10.0)
|
||||
};
|
||||
for (count, line) in lines.split('\n').enumerate()
|
||||
{
|
||||
w.elem("text", |d| {
|
||||
d.attr("y", (count as f32 * (text_font_size * 1.2)) + text_y_pos)
|
||||
d.attr("y", (count as f32 * (text_font_size * 1.2)) + text_y_pos + text_y_shift)
|
||||
.attr("x", width / 2.0)
|
||||
.attr("text-anchor", "middle")
|
||||
.attr(
|
||||
@@ -249,7 +256,7 @@ fn inner_generate_secure_join_qr_code(
|
||||
format!(
|
||||
"translate({},{})",
|
||||
(width - FOOTER_WIDTH) / 2.0,
|
||||
height - logo_offset - FOOTER_HEIGHT
|
||||
height - logo_offset - FOOTER_HEIGHT - text_y_shift
|
||||
),
|
||||
);
|
||||
})
|
||||
|
||||
@@ -695,6 +695,10 @@ fn receive_event(event: &Event) {
|
||||
"{}",
|
||||
green.paint(format!("Received CHAT_MODIFIED({})", chat))
|
||||
),
|
||||
EventType::ChatListChanged => format!(
|
||||
"{}",
|
||||
green.paint("Received CHAT_LIST_CHANGED()")
|
||||
),
|
||||
_ => format!("Received {:?}", event),
|
||||
};
|
||||
let context_names = CONTEXT_NAMES.read().unwrap();
|
||||
|
||||
Reference in New Issue
Block a user