Compare commits

...

6 Commits

Author SHA1 Message Date
link2xt
5bb0b86f6a chore(release): prepare for 2.41.0 2026-02-06 00:39:03 +00:00
B. Petersen
ed2b0e8f03 feat: use different strings for audio and video calls 2026-02-05 21:54:52 +01:00
B. Petersen
8152ff518e fix: make use of call stock strings 2026-02-05 21:54:52 +01:00
dependabot[bot]
cbcfb7087e chore(cargo): bump time from 0.3.37 to 0.3.47
Bumps [time](https://github.com/time-rs/time) from 0.3.37 to 0.3.47.
- [Release notes](https://github.com/time-rs/time/releases)
- [Changelog](https://github.com/time-rs/time/blob/main/CHANGELOG.md)
- [Commits](https://github.com/time-rs/time/compare/v0.3.37...v0.3.47)

---
updated-dependencies:
- dependency-name: time
  dependency-version: 0.3.47
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-05 19:14:15 +00:00
link2xt
396104af47 feat: do not require ShowEmails to be set to All for adding second relay 2026-02-05 19:12:08 +00:00
iequidoo
69f6727751 fix: Don't set download state to Failure if message is available on another Session's transport (#7684) 2026-02-05 18:58:52 +00:00
21 changed files with 195 additions and 107 deletions

View File

@@ -1,5 +1,21 @@
# Changelog
## [2.41.0] - 2026-02-06
### Features / Changes
- Do not require `ShowEmails` to be set to `All` for adding second relay.
- Use different strings for audio and video calls.
### Fixes
- Don't set download state to Failure if message is available on another Session's transport ([#7684](https://github.com/chatmail/core/pull/7684)).
- Make use of call stock strings.
### Miscellaneous Tasks
- cargo: Bump `time` from 0.3.37 to 0.3.47.
## [2.40.0] - 2026-02-04
### Features / Changes
@@ -7696,3 +7712,4 @@ https://github.com/chatmail/core/pulls?q=is%3Apr+is%3Aclosed
[2.38.0]: https://github.com/chatmail/core/compare/v2.37.0..v2.38.0
[2.39.0]: https://github.com/chatmail/core/compare/v2.38.0..v2.39.0
[2.40.0]: https://github.com/chatmail/core/compare/v2.39.0..v2.40.0
[2.41.0]: https://github.com/chatmail/core/compare/v2.40.0..v2.41.0

32
Cargo.lock generated
View File

@@ -1303,7 +1303,7 @@ dependencies = [
[[package]]
name = "deltachat"
version = "2.40.0"
version = "2.41.0"
dependencies = [
"anyhow",
"astral-tokio-tar",
@@ -1413,7 +1413,7 @@ dependencies = [
[[package]]
name = "deltachat-jsonrpc"
version = "2.40.0"
version = "2.41.0"
dependencies = [
"anyhow",
"async-channel 2.5.0",
@@ -1434,7 +1434,7 @@ dependencies = [
[[package]]
name = "deltachat-repl"
version = "2.40.0"
version = "2.41.0"
dependencies = [
"anyhow",
"deltachat",
@@ -1450,7 +1450,7 @@ dependencies = [
[[package]]
name = "deltachat-rpc-server"
version = "2.40.0"
version = "2.41.0"
dependencies = [
"anyhow",
"deltachat",
@@ -1479,7 +1479,7 @@ dependencies = [
[[package]]
name = "deltachat_ffi"
version = "2.40.0"
version = "2.41.0"
dependencies = [
"anyhow",
"deltachat",
@@ -1533,9 +1533,9 @@ dependencies = [
[[package]]
name = "deranged"
version = "0.3.11"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587"
dependencies = [
"powerfmt",
]
@@ -3776,9 +3776,9 @@ dependencies = [
[[package]]
name = "num-conv"
version = "0.1.0"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050"
[[package]]
name = "num-derive"
@@ -6058,31 +6058,31 @@ dependencies = [
[[package]]
name = "time"
version = "0.3.37"
version = "0.3.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
dependencies = [
"deranged",
"itoa",
"js-sys",
"num-conv",
"powerfmt",
"serde",
"serde_core",
"time-core",
"time-macros",
]
[[package]]
name = "time-core"
version = "0.1.2"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
[[package]]
name = "time-macros"
version = "0.2.19"
version = "0.2.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de"
checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215"
dependencies = [
"num-conv",
"time-core",

View File

@@ -1,6 +1,6 @@
[package]
name = "deltachat"
version = "2.40.0"
version = "2.41.0"
edition = "2024"
license = "MPL-2.0"
rust-version = "1.88"

View File

@@ -1,6 +1,6 @@
[package]
name = "deltachat_ffi"
version = "2.40.0"
version = "2.41.0"
description = "Deltachat FFI"
edition = "2018"
readme = "README.md"

View File

@@ -7544,12 +7544,6 @@ void dc_event_unref(dc_event_t* event);
/// "❤️ Seems you're enjoying Delta Chat!"… (donation request device message)
#define DC_STR_DONATION_REQUEST 193
/// "Outgoing call"
#define DC_STR_OUTGOING_CALL 194
/// "Incoming call"
#define DC_STR_INCOMING_CALL 195
/// "Declined call"
#define DC_STR_DECLINED_CALL 196
@@ -7601,6 +7595,18 @@ void dc_event_unref(dc_event_t* event);
/// Used as the first info messages in newly created classic email threads.
#define DC_STR_CHAT_UNENCRYPTED_EXPLANATON 230
/// "Outgoing audio call"
#define DC_STR_OUTGOING_AUDIO_CALL 232
/// "Outgoing video call"
#define DC_STR_OUTGOING_VIDEO_CALL 233
/// "Incoming audio call"
#define DC_STR_INCOMING_AUDIO_CALL 234
/// "Incoming video call"
#define DC_STR_INCOMING_VIDEO_CALL 235
/**
* @}
*/

View File

@@ -1,6 +1,6 @@
[package]
name = "deltachat-jsonrpc"
version = "2.40.0"
version = "2.41.0"
description = "DeltaChat JSON-RPC API"
edition = "2021"
license = "MPL-2.0"

View File

@@ -54,5 +54,5 @@
},
"type": "module",
"types": "dist/deltachat.d.ts",
"version": "2.40.0"
"version": "2.41.0"
}

View File

@@ -1,6 +1,6 @@
[package]
name = "deltachat-repl"
version = "2.40.0"
version = "2.41.0"
license = "MPL-2.0"
edition = "2021"
repository = "https://github.com/chatmail/core"

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "deltachat-rpc-client"
version = "2.40.0"
version = "2.41.0"
license = "MPL-2.0"
description = "Python client for Delta Chat core JSON-RPC interface"
classifiers = [

View File

@@ -1,6 +1,7 @@
import pytest
from deltachat_rpc_client import EventType
from deltachat_rpc_client.const import DownloadState
from deltachat_rpc_client.rpc import JsonRpcError
@@ -37,8 +38,8 @@ def test_add_second_address(acfactory) -> None:
with pytest.raises(JsonRpcError):
account.set_config(option, "1")
with pytest.raises(JsonRpcError):
account.set_config("show_emails", "0")
# show_emails does not matter for multi-relay, can be set to anything
account.set_config("show_emails", "0")
@pytest.mark.parametrize("key", ["mvbox_move", "only_fetch_mvbox"])
@@ -57,8 +58,8 @@ def test_no_second_transport_with_mvbox(acfactory, key) -> None:
account.add_transport_from_qr(qr)
def test_no_second_transport_without_classic_emails(acfactory) -> None:
"""Test that second transport cannot be configured if classic emails are not fetched."""
def test_second_transport_without_classic_emails(acfactory) -> None:
"""Test that second transport can be configured if classic emails are not fetched."""
account = acfactory.new_configured_account()
assert len(account.list_transports()) == 1
@@ -67,8 +68,7 @@ def test_no_second_transport_without_classic_emails(acfactory) -> None:
qr = acfactory.get_account_qr()
account.set_config("show_emails", "0")
with pytest.raises(JsonRpcError):
account.add_transport_from_qr(qr)
account.add_transport_from_qr(qr)
def test_change_address(acfactory) -> None:
@@ -120,6 +120,33 @@ def test_change_address(acfactory) -> None:
assert sender_addr2 == new_alice_addr
def test_download_on_demand(acfactory) -> None:
alice, bob = acfactory.get_online_accounts(2)
alice.set_config("download_limit", "1")
alice.stop_io()
qr = acfactory.get_account_qr()
alice.add_transport_from_qr(qr)
alice.start_io()
alice.create_chat(bob)
chat_bob_alice = bob.create_chat(alice)
chat_bob_alice.send_message(file="../test-data/image/screenshot.jpg")
msg = alice.wait_for_incoming_msg()
snapshot = msg.get_snapshot()
assert snapshot.download_state == DownloadState.AVAILABLE
chat_id = snapshot.chat_id
# Actually the message isn't available yet. Wait somehow for the post-message to arrive.
chat_bob_alice.send_message("Now you can download my previous message")
alice.wait_for_incoming_msg()
alice._rpc.download_full_message(alice.id, msg.id)
for dstate in [DownloadState.IN_PROGRESS, DownloadState.DONE]:
event = alice.wait_for_event(EventType.MSGS_CHANGED)
assert event.chat_id == chat_id
assert event.msg_id == msg.id
assert msg.get_snapshot().download_state == dstate
@pytest.mark.parametrize("is_chatmail", ["0", "1"])
def test_mvbox_move_first_transport(acfactory, is_chatmail) -> None:
"""Test that mvbox_move is disabled by default even for non-chatmail accounts.

View File

@@ -1,6 +1,6 @@
[package]
name = "deltachat-rpc-server"
version = "2.40.0"
version = "2.41.0"
description = "DeltaChat JSON-RPC server"
edition = "2021"
readme = "README.md"

View File

@@ -15,5 +15,5 @@
},
"type": "module",
"types": "index.d.ts",
"version": "2.40.0"
"version": "2.41.0"
}

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "deltachat"
version = "2.40.0"
version = "2.41.0"
license = "MPL-2.0"
description = "Python bindings for the Delta Chat Core library using CFFI against the Rust-implemented libdeltachat"
readme = "README.rst"

View File

@@ -1 +1 @@
2026-02-04
2026-02-06

View File

@@ -15,6 +15,7 @@ use crate::message::{Message, MsgId, Viewtype};
use crate::mimeparser::{MimeMessage, SystemMessage};
use crate::net::dns::lookup_host_with_cache;
use crate::param::Param;
use crate::stock_str;
use crate::tools::{normalize_text, time};
use anyhow::{Context as _, Result, ensure};
use deltachat_derive::{FromSql, ToSql};
@@ -102,10 +103,14 @@ impl CallInfo {
};
if self.is_incoming() {
self.update_text(context, &format!("Incoming call\n{duration}"))
let incoming_call_str =
stock_str::incoming_call(context, self.has_video_initially()).await;
self.update_text(context, &format!("{incoming_call_str}\n{duration}"))
.await?;
} else {
self.update_text(context, &format!("Outgoing call\n{duration}"))
let outgoing_call_str =
stock_str::outgoing_call(context, self.has_video_initially()).await;
self.update_text(context, &format!("{outgoing_call_str}\n{duration}"))
.await?;
}
Ok(())
@@ -200,9 +205,10 @@ impl Context {
);
ensure!(!chat.is_self_talk(), "Cannot call self");
let outgoing_call_str = stock_str::outgoing_call(self, has_video_initially).await;
let mut call = Message {
viewtype: Viewtype::Call,
text: "Outgoing call".into(),
text: outgoing_call_str,
..Default::default()
};
call.param.set(Param::WebrtcRoom, &place_call_info);
@@ -275,10 +281,12 @@ impl Context {
if !call.is_accepted() {
if call.is_incoming() {
call.mark_as_ended(self).await?;
call.update_text(self, "Declined call").await?;
let declined_call_str = stock_str::declined_call(self).await;
call.update_text(self, &declined_call_str).await?;
} else {
call.mark_as_canceled(self).await?;
call.update_text(self, "Canceled call").await?;
let canceled_call_str = stock_str::canceled_call(self).await;
call.update_text(self, &canceled_call_str).await?;
}
} else {
call.mark_as_ended(self).await?;
@@ -320,10 +328,12 @@ impl Context {
if !call.is_accepted() && !call.is_ended() {
if call.is_incoming() {
call.mark_as_canceled(&context).await?;
call.update_text(&context, "Missed call").await?;
let missed_call_str = stock_str::missed_call(&context).await;
call.update_text(&context, &missed_call_str).await?;
} else {
call.mark_as_ended(&context).await?;
call.update_text(&context, "Canceled call").await?;
let canceled_call_str = stock_str::canceled_call(&context).await;
call.update_text(&context, &canceled_call_str).await?;
}
context.emit_msgs_changed(call.msg.chat_id, call_id);
context.emit_event(EventType::CallEnded {
@@ -348,10 +358,13 @@ impl Context {
if call.is_incoming() {
if call.is_stale() {
call.update_text(self, "Missed call").await?;
let missed_call_str = stock_str::missed_call(self).await;
call.update_text(self, &missed_call_str).await?;
self.emit_incoming_msg(call.msg.chat_id, call_id); // notify missed call
} else {
call.update_text(self, "Incoming call").await?;
let incoming_call_str =
stock_str::incoming_call(self, call.has_video_initially()).await;
call.update_text(self, &incoming_call_str).await?;
self.emit_msgs_changed(call.msg.chat_id, call_id); // ringing calls are not additionally notified
let can_call_me = match who_can_call_me(self).await? {
WhoCanCallMe::Contacts => ChatIdBlocked::lookup_by_contact(self, from_id)
@@ -391,7 +404,9 @@ impl Context {
));
}
} else {
call.update_text(self, "Outgoing call").await?;
let outgoing_call_str =
stock_str::outgoing_call(self, call.has_video_initially()).await;
call.update_text(self, &outgoing_call_str).await?;
self.emit_msgs_changed(call.msg.chat_id, call_id);
}
} else {
@@ -441,19 +456,23 @@ impl Context {
if call.is_incoming() {
if from_id == ContactId::SELF {
call.mark_as_ended(self).await?;
call.update_text(self, "Declined call").await?;
let declined_call_str = stock_str::declined_call(self).await;
call.update_text(self, &declined_call_str).await?;
} else {
call.mark_as_canceled(self).await?;
call.update_text(self, "Missed call").await?;
let missed_call_str = stock_str::missed_call(self).await;
call.update_text(self, &missed_call_str).await?;
}
} else {
// outgoing
if from_id == ContactId::SELF {
call.mark_as_canceled(self).await?;
call.update_text(self, "Canceled call").await?;
let canceled_call_str = stock_str::canceled_call(self).await;
call.update_text(self, &canceled_call_str).await?;
} else {
call.mark_as_ended(self).await?;
call.update_text(self, "Declined call").await?;
let declined_call_str = stock_str::declined_call(self).await;
call.update_text(self, &declined_call_str).await?;
}
}
} else {

View File

@@ -62,7 +62,7 @@ async fn setup_call() -> Result<CallSetup> {
assert!(!info.is_accepted());
assert_eq!(info.place_call_info, PLACE_INFO);
assert_eq!(info.has_video_initially(), true);
assert_text(t, m.id, "Outgoing call").await?;
assert_text(t, m.id, "Outgoing video call").await?;
assert_eq!(call_state(t, m.id).await?, CallState::Alerting);
}
@@ -84,7 +84,7 @@ async fn setup_call() -> Result<CallSetup> {
assert!(!info.is_accepted());
assert_eq!(info.place_call_info, PLACE_INFO);
assert_eq!(info.has_video_initially(), true);
assert_text(t, m.id, "Incoming call").await?;
assert_text(t, m.id, "Incoming video call").await?;
assert_eq!(call_state(t, m.id).await?, CallState::Alerting);
}
@@ -115,7 +115,7 @@ async fn accept_call() -> Result<CallSetup> {
// Bob accepts the incoming call
bob.accept_incoming_call(bob_call.id, ACCEPT_INFO.to_string())
.await?;
assert_text(&bob, bob_call.id, "Incoming call").await?;
assert_text(&bob, bob_call.id, "Incoming video call").await?;
bob.evtracker
.get_matching(|evt| matches!(evt, EventType::IncomingCallAccepted { .. }))
.await;
@@ -129,7 +129,7 @@ async fn accept_call() -> Result<CallSetup> {
assert_eq!(call_state(&bob, bob_call.id).await?, CallState::Active);
bob2.recv_msg_trash(&sent2).await;
assert_text(&bob, bob_call.id, "Incoming call").await?;
assert_text(&bob, bob_call.id, "Incoming video call").await?;
bob2.evtracker
.get_matching(|evt| matches!(evt, EventType::IncomingCallAccepted { .. }))
.await;
@@ -142,7 +142,7 @@ async fn accept_call() -> Result<CallSetup> {
// Alice receives the acceptance message
alice.recv_msg_trash(&sent2).await;
assert_text(&alice, alice_call.id, "Outgoing call").await?;
assert_text(&alice, alice_call.id, "Outgoing video call").await?;
let ev = alice
.evtracker
.get_matching(|evt| matches!(evt, EventType::OutgoingCallAccepted { .. }))
@@ -164,7 +164,7 @@ async fn accept_call() -> Result<CallSetup> {
assert_eq!(call_state(&alice, alice_call.id).await?, CallState::Active);
alice2.recv_msg_trash(&sent2).await;
assert_text(&alice2, alice2_call.id, "Outgoing call").await?;
assert_text(&alice2, alice2_call.id, "Outgoing video call").await?;
alice2
.evtracker
.get_matching(|evt| matches!(evt, EventType::OutgoingCallAccepted { .. }))
@@ -203,7 +203,7 @@ async fn test_accept_call_callee_ends() -> Result<()> {
// Bob has accepted the call and also ends it
bob.end_call(bob_call.id).await?;
assert_text(&bob, bob_call.id, "Incoming call\n<1 minute").await?;
assert_text(&bob, bob_call.id, "Incoming video call\n<1 minute").await?;
bob.evtracker
.get_matching(|evt| matches!(evt, EventType::CallEnded { .. }))
.await;
@@ -214,7 +214,7 @@ async fn test_accept_call_callee_ends() -> Result<()> {
));
bob2.recv_msg_trash(&sent3).await;
assert_text(&bob2, bob2_call.id, "Incoming call\n<1 minute").await?;
assert_text(&bob2, bob2_call.id, "Incoming video call\n<1 minute").await?;
bob2.evtracker
.get_matching(|evt| matches!(evt, EventType::CallEnded { .. }))
.await;
@@ -225,7 +225,7 @@ async fn test_accept_call_callee_ends() -> Result<()> {
// Alice receives the ending message
alice.recv_msg_trash(&sent3).await;
assert_text(&alice, alice_call.id, "Outgoing call\n<1 minute").await?;
assert_text(&alice, alice_call.id, "Outgoing video call\n<1 minute").await?;
alice
.evtracker
.get_matching(|evt| matches!(evt, EventType::CallEnded { .. }))
@@ -236,7 +236,7 @@ async fn test_accept_call_callee_ends() -> Result<()> {
));
alice2.recv_msg_trash(&sent3).await;
assert_text(&alice2, alice2_call.id, "Outgoing call\n<1 minute").await?;
assert_text(&alice2, alice2_call.id, "Outgoing video call\n<1 minute").await?;
alice2
.evtracker
.get_matching(|evt| matches!(evt, EventType::CallEnded { .. }))
@@ -266,7 +266,7 @@ async fn test_accept_call_caller_ends() -> Result<()> {
// Bob has accepted the call but Alice ends it
alice.end_call(alice_call.id).await?;
assert_text(&alice, alice_call.id, "Outgoing call\n<1 minute").await?;
assert_text(&alice, alice_call.id, "Outgoing video call\n<1 minute").await?;
alice
.evtracker
.get_matching(|evt| matches!(evt, EventType::CallEnded { .. }))
@@ -278,7 +278,7 @@ async fn test_accept_call_caller_ends() -> Result<()> {
));
alice2.recv_msg_trash(&sent3).await;
assert_text(&alice2, alice2_call.id, "Outgoing call\n<1 minute").await?;
assert_text(&alice2, alice2_call.id, "Outgoing video call\n<1 minute").await?;
alice2
.evtracker
.get_matching(|evt| matches!(evt, EventType::CallEnded { .. }))
@@ -290,7 +290,7 @@ async fn test_accept_call_caller_ends() -> Result<()> {
// Bob receives the ending message
bob.recv_msg_trash(&sent3).await;
assert_text(&bob, bob_call.id, "Incoming call\n<1 minute").await?;
assert_text(&bob, bob_call.id, "Incoming video call\n<1 minute").await?;
bob.evtracker
.get_matching(|evt| matches!(evt, EventType::CallEnded { .. }))
.await;
@@ -300,7 +300,7 @@ async fn test_accept_call_caller_ends() -> Result<()> {
));
bob2.recv_msg_trash(&sent3).await;
assert_text(&bob2, bob2_call.id, "Incoming call\n<1 minute").await?;
assert_text(&bob2, bob2_call.id, "Incoming video call\n<1 minute").await?;
bob2.evtracker
.get_matching(|evt| matches!(evt, EventType::CallEnded { .. }))
.await;
@@ -420,7 +420,7 @@ async fn test_caller_cancels_call() -> Result<()> {
// Test that message summary says it is a missed call.
let bob_call_msg = Message::load_from_db(&bob, bob_call.id).await?;
let summary = bob_call_msg.get_summary(&bob, None).await?;
assert_eq!(summary.text, "📞 Missed call");
assert_eq!(summary.text, "🎥 Missed call");
bob2.recv_msg_trash(&sent3).await;
assert_text(&bob2, bob2_call.id, "Missed call").await?;

View File

@@ -712,12 +712,7 @@ impl Context {
Self::check_config(key, value)?;
let n_transports = self.count_transports().await?;
if n_transports > 1
&& matches!(
key,
Config::MvboxMove | Config::OnlyFetchMvbox | Config::ShowEmails
)
{
if n_transports > 1 && matches!(key, Config::MvboxMove | Config::OnlyFetchMvbox) {
bail!("Cannot reconfigure {key} when multiple transports are configured");
}

View File

@@ -286,11 +286,6 @@ impl Context {
"To use additional relays, disable the legacy option \"Settings / Advanced / Move automatically to DeltaChat Folder\"."
);
}
if self.get_config(Config::ShowEmails).await?.as_deref() != Some("2") {
bail!(
"To use additional relays, set the legacy option \"Settings / Advanced / Show Classic Emails\" to \"All\"."
);
}
if self
.sql

View File

@@ -99,7 +99,8 @@ impl MsgId {
Ok(())
}
/// Updates the message download state. Returns `Ok` if the message doesn't exist anymore.
/// Updates the message download state. Returns `Ok` if the message doesn't exist anymore or has
/// the download state up to date.
pub(crate) async fn update_download_state(
self,
context: &Context,
@@ -108,7 +109,7 @@ impl MsgId {
if context
.sql
.execute(
"UPDATE msgs SET download_state=? WHERE id=?;",
"UPDATE msgs SET download_state=? WHERE id=? AND download_state<>?1",
(download_state, self),
)
.await?
@@ -135,42 +136,46 @@ impl Message {
}
}
/// Actually download a message partially downloaded before.
/// Actually downloads a message partially downloaded before if the message is available on the
/// session transport, in which case returns `Some`. If the message is available on another
/// transport, returns `None`.
///
/// Most messages are downloaded automatically on fetch instead.
pub(crate) async fn download_msg(
context: &Context,
rfc724_mid: String,
session: &mut Session,
) -> Result<()> {
) -> Result<Option<()>> {
let transport_id = session.transport_id();
let row = context
.sql
.query_row_optional(
"SELECT uid, folder FROM imap
WHERE rfc724_mid=?
AND transport_id=?
AND target!=''",
"SELECT uid, folder, transport_id FROM imap
WHERE rfc724_mid=? AND target!=''
ORDER BY transport_id=? DESC LIMIT 1",
(&rfc724_mid, transport_id),
|row| {
let server_uid: u32 = row.get(0)?;
let server_folder: String = row.get(1)?;
Ok((server_uid, server_folder))
let msg_transport_id: u32 = row.get(2)?;
Ok((server_uid, server_folder, msg_transport_id))
},
)
.await?;
let Some((server_uid, server_folder)) = row else {
let Some((server_uid, server_folder, msg_transport_id)) = row else {
// No IMAP record found, we don't know the UID and folder.
return Err(anyhow!(
"IMAP location for {rfc724_mid:?} post-message is unknown"
));
};
if msg_transport_id != transport_id {
return Ok(None);
}
session
.fetch_single_msg(context, &server_folder, server_uid, rfc724_mid)
.await?;
Ok(())
Ok(Some(()))
}
impl Session {
@@ -272,7 +277,7 @@ pub(crate) async fn download_msgs(context: &Context, session: &mut Session) -> R
for rfc724_mid in &rfc724_mids {
let res = download_msg(context, rfc724_mid.clone(), session).await;
if res.is_ok() {
if let Ok(Some(())) = res {
delete_from_downloads(context, rfc724_mid).await?;
delete_from_available_post_msgs(context, rfc724_mid).await?;
}
@@ -327,7 +332,7 @@ pub(crate) async fn download_known_post_messages_without_pre_message(
// The message may be in the wrong order,
// but at least we have it at all.
let res = download_msg(context, rfc724_mid.clone(), session).await;
if res.is_ok() {
if let Ok(Some(())) = res {
delete_from_available_post_msgs(context, rfc724_mid).await?;
}
if let Err(err) = res {

View File

@@ -369,12 +369,6 @@ Help keeping us to keep Delta Chat independent and make it more awesome in the f
https://delta.chat/donate"))]
DonationRequest = 193,
#[strum(props(fallback = "Outgoing call"))]
OutgoingCall = 194,
#[strum(props(fallback = "Incoming call"))]
IncomingCall = 195,
#[strum(props(fallback = "Declined call"))]
DeclinedCall = 196,
@@ -417,6 +411,18 @@ https://delta.chat/donate"))]
fallback = "You are using the legacy option \"Settings → Advanced → Move automatically to DeltaChat Folder\".\n\nThis option will be removed in a few weeks and you should disable it already today.\n\nIf having chat messages mixed into your inbox is a problem, see https://delta.chat/legacy-move"
))]
MvboxMoveDeprecation = 231,
#[strum(props(fallback = "Outgoing audio call"))]
OutgoingAudioCall = 232,
#[strum(props(fallback = "Outgoing video call"))]
OutgoingVideoCall = 233,
#[strum(props(fallback = "Incoming audio call"))]
IncomingAudioCall = 234,
#[strum(props(fallback = "Incoming video call"))]
IncomingVideoCall = 235,
}
impl StockMessage {
@@ -762,14 +768,30 @@ pub(crate) async fn donation_request(context: &Context) -> String {
translated(context, StockMessage::DonationRequest).await
}
/// Stock string: `Outgoing call`.
pub(crate) async fn outgoing_call(context: &Context) -> String {
translated(context, StockMessage::OutgoingCall).await
/// Stock string: `Outgoing video call` or `Outgoing audio call`.
pub(crate) async fn outgoing_call(context: &Context, has_video: bool) -> String {
translated(
context,
if has_video {
StockMessage::OutgoingVideoCall
} else {
StockMessage::OutgoingAudioCall
},
)
.await
}
/// Stock string: `Incoming call`.
pub(crate) async fn incoming_call(context: &Context) -> String {
translated(context, StockMessage::IncomingCall).await
/// Stock string: `Incoming video call` or `Incoming audio call`.
pub(crate) async fn incoming_call(context: &Context, has_video: bool) -> String {
translated(
context,
if has_video {
StockMessage::IncomingVideoCall
} else {
StockMessage::IncomingAudioCall
},
)
.await
}
/// Stock string: `Declined call`.

View File

@@ -247,16 +247,18 @@ impl Message {
append_text = true;
}
Viewtype::Call => {
let call_info = context.load_call_by_id(self.id).await.unwrap_or(None);
let has_video = call_info.is_some_and(|c| c.has_video_initially());
let call_state = call_state(context, self.id)
.await
.unwrap_or(CallState::Alerting);
emoji = Some("📞");
emoji = Some(if has_video { "🎥" } else { "📞" });
type_name = Some(match call_state {
CallState::Alerting | CallState::Active | CallState::Completed { .. } => {
if self.from_id == ContactId::SELF {
stock_str::outgoing_call(context).await
stock_str::outgoing_call(context, has_video).await
} else {
stock_str::incoming_call(context).await
stock_str::incoming_call(context, has_video).await
}
}
CallState::Missed => stock_str::missed_call(context).await,