Compare commits

...
Author SHA1 Message Date
Simon Laux 3e35c23f29 add chatlist changed event 2021-11-29 17:56:54 +01:00
bjoern 7f97768c56 prepare 1.68 (#2844)
* update changelog for 1.68.0

* bump version to 1.68.0
2021-11-28 12:22:14 +01:00
dependabot[bot] ecd548a7aa Merge pull request #2829 from deltachat/dependabot/cargo/libc-0.2.108 2021-11-27 19:03:56 +00:00
dependabot[bot] 62efb0795b Merge pull request #2830 from deltachat/dependabot/cargo/anyhow-1.0.48 2021-11-27 19:03:22 +00:00
bjoern 53f042ee08 tweak qr svg (#2842)
* repl: allow groupname arguments with more than one word (came over that when testing qr codes)

* calcualte text-size from the real number of lines

* shift text and watermark apart when text get longer

* make clippy happy
2021-11-27 18:57:46 +01:00
link2xt 6ce97bd0cd Merge fixes for chat assignment when forwarding messages
GitHub PR #2843
2021-11-27 00:00:00 +00:00
link2xt c29149e74c Add group forwarding test 2021-11-27 00:00:00 +00:00
link2xt 487f7593ce Reset In-Reply-To when forwarding a message 2021-11-27 00:00:00 +00:00
link2xt 6b3b33d2a0 Test forwarded quoted messages 2021-11-27 00:00:00 +00:00
link2xt 2d70ccc2bf Do not return a quoted message for forwarded messages
For forwarded messages, parent message is not a quoted message.
2021-11-27 00:00:00 +00:00
link2xt e90fc9504a Test get_parent_message 2021-11-27 00:00:00 +00:00
link2xt 5108314c03 Do not return trashed messages from get_rfc724_mid_in_list
This function is used to lookup the chat by `References` and
`In-Reply-To` header, so it does not make sense to return trashed
message when there is another non-trashed message in one of these
headers with a real chat ID.
2021-11-27 00:00:00 +00:00
Hocuri f2b86a1c0f Update aeap-mvp.rst 2021-11-25 16:18:30 +01:00
Hocuri 9583d41446 Add a draft how an AEAP MVP could look 2021-11-25 16:18:30 +01:00
dependabot[bot] e594064f93 cargo: bump anyhow from 1.0.47 to 1.0.48
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.47 to 1.0.48.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.47...1.0.48)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-25 14:30:59 +00:00
dependabot[bot] eb610c27bf cargo: bump libc from 0.2.107 to 0.2.108
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.107 to 0.2.108.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.107...0.2.108)

---
updated-dependencies:
- dependency-name: libc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-22 21:11:56 +00:00
18 changed files with 294 additions and 25 deletions
+15
View File
@@ -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
Generated
+6 -6
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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"
+8
View File
@@ -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.
+4 -1
View File
@@ -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
View 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.
+8 -1
View File
@@ -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");
}
+6
View File
@@ -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);
}
+103 -1
View File
@@ -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
+1
View File
@@ -314,6 +314,7 @@ impl Context {
msg_id: MsgId::new(0),
chat_id: ChatId::new(0),
});
self.emit_event(EventType::ChatListChanged);
ret
}
Config::Displayname => {
+6 -2
View File
@@ -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.
+70 -1
View File
@@ -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(())
}
}
+2
View File
@@ -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);
}
}
}
+7
View File
@@ -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.
+2 -1
View File
@@ -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,
+17 -10
View File
@@ -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
),
);
})
+4
View File
@@ -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();