mirror of
https://github.com/chatmail/core.git
synced 2026-09-22 04:58:47 +03:00
Compare commits
7 Commits
v1.133.0
...
simon/UICh
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f22263953f | ||
|
|
2c3734153a | ||
|
|
7483aa532b | ||
|
|
a76a7ae781 | ||
|
|
f4caf1688b | ||
|
|
6442e13a1a | ||
|
|
a048d6b0d1 |
57
CHANGELOG.md
57
CHANGELOG.md
@@ -1,61 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## [1.133.0] - 2024-01-14
|
||||
|
||||
### Features / Changes
|
||||
|
||||
- Securejoin protocol implementation refinements
|
||||
- Track forward and backward verification separately ([#5089](https://github.com/deltachat/deltachat-core-rust/pull/5089)) to avoid inconsistent states.
|
||||
- Mark 1:1 chat as verified for Bob early. 1:1 chat with Alice is verified as soon as Alice's key is verified rather than at the end of the protocol.
|
||||
- Put Message-ID into hidden headers and take it from there on receiver ([#4798](https://github.com/deltachat/deltachat-core-rust/pull/4798)). This works around servers which generate their own Message-ID and overwrite the one generated by Delta Chat.
|
||||
- deltachat-repl: Enable INFO logging by default and add timestamps.
|
||||
- Add `ConfigSynced` (`DC_EVENT_CONFIG_SYNCED`) event which is emitted when configuration is changed via synchronization message or synchronization message for configuration is sent. UI may refresh elments based on the configuration key which is a part of the event.
|
||||
- Sync contact creation/rename across devices ([#5163](https://github.com/deltachat/deltachat-core-rust/pull/5163)).
|
||||
- Encrypt MDNs ([#5175](https://github.com/deltachat/deltachat-core-rust/pull/5175)).
|
||||
- Only try to configure non-strict TLS checks if explicitly set ([#5181](https://github.com/deltachat/deltachat-core-rust/pull/5181)).
|
||||
|
||||
### Build system
|
||||
|
||||
- Use released version of iroh 0.4.2 for "setup second device" feature.
|
||||
|
||||
### CI
|
||||
|
||||
- Update to Rust 1.75.0.
|
||||
- Downgrade `chai` from 4.4.0 to 4.3.10.
|
||||
|
||||
### Documentation
|
||||
|
||||
- Add a link <https://www.ietf.org/archive/id/draft-bucksch-autoconfig-00.html> to autoconfig RFC draft.
|
||||
- Update securejoin link in `standards.md` from <https://countermitm.readthedocs.io/> to <https://securejoin.readthedocs.io>.
|
||||
- Restore "Constants" page in Doxygen >=1.9.8
|
||||
|
||||
### Fixes
|
||||
|
||||
- imap: Limit the rate of LOGIN attempts rather than connection attempts. This is to avoid having to wait for rate limiter right after switching from a bad or offline network to a working network while still guarding against reconnection loop.
|
||||
- Do not ignore `peerstate.save_to_db()` errors.
|
||||
- securejoin: Mark 1:1s as protected regardless of the Config::VerifiedOneOnOneChats.
|
||||
- Delete received outgoing messages from SMTP queue ([#5115](https://github.com/deltachat/deltachat-core-rust/pull/5115)).
|
||||
- imap: Fail fast on `LIST` errors to avoid busy loop when connection is lost.
|
||||
- Split SMTP jobs already in `chat::create_send_msg_jobs()` ([#5115](https://github.com/deltachat/deltachat-core-rust/pull/5115)).
|
||||
- Do not remove contents from unencrypted [Schleuder](https://schleuder.org/) mailing lists messages.
|
||||
- Reset message error when scheduling resending ([#5119](https://github.com/deltachat/deltachat-core-rust/pull/5119)).
|
||||
- Emit events more reliably when starting and stopping I/O ([#5101](https://github.com/deltachat/deltachat-core-rust/pull/5101)).
|
||||
- Fix timestamp of chat protection info message for correct message ordering after restoring a backup ([#5088](https://github.com/deltachat/deltachat-core-rust/pull/5088)).
|
||||
|
||||
### Refactor
|
||||
|
||||
- sql: Recreate `config` table with UNIQUE constraint.
|
||||
- sql: Recreate `keypairs` table to remove unused `addr` and `created` fields and move `is_default` flag to `config` table.
|
||||
- Send `Secure-Join-Fingerprint` only in `*-request-with-auth`.
|
||||
|
||||
### Tests
|
||||
|
||||
- Test joining non-protected group.
|
||||
- Test that read receipts don't degrade encryption.
|
||||
- Test that changing default private key breaks backward verification.
|
||||
- Test recovery from lost vc-contact-confirm.
|
||||
- Use `wait_for_incoming_msg_event()` more.
|
||||
|
||||
## [1.132.1] - 2023-12-12
|
||||
|
||||
### Features / Changes
|
||||
@@ -3413,4 +3357,3 @@ https://github.com/deltachat/deltachat-core-rust/pulls?q=is%3Apr+is%3Aclosed
|
||||
[1.131.9]: https://github.com/deltachat/deltachat-core-rust/compare/v1.131.8...v1.131.9
|
||||
[1.132.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.131.9...v1.132.0
|
||||
[1.132.1]: https://github.com/deltachat/deltachat-core-rust/compare/v1.132.0...v1.132.1
|
||||
[1.133.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.132.1...v1.133.0
|
||||
|
||||
56
Cargo.lock
generated
56
Cargo.lock
generated
@@ -738,11 +738,13 @@ checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
|
||||
|
||||
[[package]]
|
||||
name = "clipboard-win"
|
||||
version = "5.0.0"
|
||||
version = "4.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c57002a5d9be777c1ef967e33674dac9ebd310d8893e4e3437b14d5f0f6372cc"
|
||||
checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362"
|
||||
dependencies = [
|
||||
"error-code",
|
||||
"str-buf",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1089,7 +1091,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat"
|
||||
version = "1.133.0"
|
||||
version = "1.132.1"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"anyhow",
|
||||
@@ -1108,7 +1110,7 @@ dependencies = [
|
||||
"encoded-words",
|
||||
"escaper",
|
||||
"fast-socks5",
|
||||
"fd-lock",
|
||||
"fd-lock 4.0.2",
|
||||
"format-flowed",
|
||||
"futures",
|
||||
"futures-lite",
|
||||
@@ -1167,7 +1169,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat-jsonrpc"
|
||||
version = "1.133.0"
|
||||
version = "1.132.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-channel 2.1.1",
|
||||
@@ -1191,7 +1193,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat-repl"
|
||||
version = "1.133.0"
|
||||
version = "1.132.1"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"anyhow",
|
||||
@@ -1206,7 +1208,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat-rpc-server"
|
||||
version = "1.133.0"
|
||||
version = "1.132.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"deltachat",
|
||||
@@ -1231,7 +1233,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat_ffi"
|
||||
version = "1.133.0"
|
||||
version = "1.132.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"deltachat",
|
||||
@@ -1775,9 +1777,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "error-code"
|
||||
version = "3.0.0"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "281e452d3bad4005426416cdba5ccfd4f5c1280e10099e21db27f7c1c28347fc"
|
||||
checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"str-buf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "escaper"
|
||||
@@ -1856,6 +1862,17 @@ version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
|
||||
|
||||
[[package]]
|
||||
name = "fd-lock"
|
||||
version = "3.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"rustix",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fd-lock"
|
||||
version = "4.0.2"
|
||||
@@ -2988,11 +3005,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.27.1"
|
||||
version = "0.26.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
|
||||
checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"bitflags 1.3.2",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
]
|
||||
@@ -4208,20 +4225,21 @@ checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
|
||||
|
||||
[[package]]
|
||||
name = "rustyline"
|
||||
version = "13.0.0"
|
||||
version = "12.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02a2d683a4ac90aeef5b1013933f6d977bd37d51ff3f4dad829d4931a7e6be86"
|
||||
checksum = "994eca4bca05c87e86e15d90fc7a91d1be64b4482b38cb2d27474568fe7c9db9"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"cfg-if",
|
||||
"clipboard-win",
|
||||
"fd-lock",
|
||||
"fd-lock 3.0.13",
|
||||
"home",
|
||||
"libc",
|
||||
"log",
|
||||
"memchr",
|
||||
"nix",
|
||||
"radix_trie",
|
||||
"scopeguard",
|
||||
"unicode-segmentation",
|
||||
"unicode-width",
|
||||
"utf8parse",
|
||||
@@ -4672,6 +4690,12 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "str-buf"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat"
|
||||
version = "1.133.0"
|
||||
version = "1.132.1"
|
||||
edition = "2021"
|
||||
license = "MPL-2.0"
|
||||
rust-version = "1.70"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat_ffi"
|
||||
version = "1.133.0"
|
||||
version = "1.132.1"
|
||||
description = "Deltachat FFI"
|
||||
edition = "2018"
|
||||
readme = "README.md"
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<tab type="hierarchy" visible="no" title="" intro=""/>
|
||||
<tab type="classmembers" visible="no" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="topics" visible="yes" title="Constants" intro="Here is a list of constants:"/>
|
||||
<tab type="modules" visible="yes" title="Constants" intro="Here is a list of constants:"/>
|
||||
<tab type="pages" visible="yes" title="" intro=""/>
|
||||
<tab type="namespaces" visible="yes" title="">
|
||||
<tab type="namespacelist" visible="yes" title="" intro=""/>
|
||||
|
||||
@@ -6206,18 +6206,6 @@ void dc_event_unref(dc_event_t* event);
|
||||
#define DC_EVENT_SELFAVATAR_CHANGED 2110
|
||||
|
||||
|
||||
/**
|
||||
* A multi-device synced config value changed. Maybe the app needs to refresh smth. For uniformity
|
||||
* this is emitted on the source device too. The value isn't reported, otherwise it would be logged
|
||||
* which might not be good for privacy. You can get the new value with
|
||||
* `dc_get_config(context, data2)`.
|
||||
*
|
||||
* @param data1 0
|
||||
* @param data2 (char*) Configuration key.
|
||||
*/
|
||||
#define DC_EVENT_CONFIG_SYNCED 2111
|
||||
|
||||
|
||||
/**
|
||||
* webxdc status update received.
|
||||
* To get the received status update, use dc_get_webxdc_status_updates() with
|
||||
@@ -6242,6 +6230,22 @@ void dc_event_unref(dc_event_t* event);
|
||||
|
||||
#define DC_EVENT_WEBXDC_INSTANCE_DELETED 2121
|
||||
|
||||
/**
|
||||
* Inform UI that Order (and content as in chat ids) of the chatlist changed.
|
||||
*
|
||||
* Sometimes this is emitted together with `DC_EVENT_UI_CHATLIST_ITEM_CHANGED` such as on `DC_EVENT_INCOMING_MSG`.
|
||||
*/
|
||||
|
||||
#define DC_EVENT_UI_CHATLIST_CHANGED 2200
|
||||
|
||||
/**
|
||||
* Inform UI that all or a single chat list item changed and needs to be rerendered
|
||||
* If `chat_id` is set to 0, then all currently visible chats need to be rerendered, and all not-visible items need to be cleared from cache if the UI has a cache.
|
||||
*
|
||||
* @param data1 (int) chat_id chat id of chatlist item to be rerendered, if chat_id = 0 all (cached & visible) items need to be rerendered
|
||||
*/
|
||||
|
||||
#define DC_EVENT_UI_CHATLIST_ITEM_CHANGED 2201
|
||||
|
||||
/**
|
||||
* @}
|
||||
|
||||
@@ -556,9 +556,10 @@ pub unsafe extern "C" fn dc_event_get_id(event: *mut dc_event_t) -> libc::c_int
|
||||
EventType::SecurejoinJoinerProgress { .. } => 2061,
|
||||
EventType::ConnectivityChanged => 2100,
|
||||
EventType::SelfavatarChanged => 2110,
|
||||
EventType::ConfigSynced { .. } => 2111,
|
||||
EventType::WebxdcStatusUpdate { .. } => 2120,
|
||||
EventType::WebxdcInstanceDeleted { .. } => 2121,
|
||||
EventType::UIChatListChanged => 2200,
|
||||
EventType::UIChatListItemChanged { .. } => 2201,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -584,9 +585,9 @@ pub unsafe extern "C" fn dc_event_get_data1_int(event: *mut dc_event_t) -> libc:
|
||||
| EventType::Error(_)
|
||||
| EventType::ConnectivityChanged
|
||||
| EventType::SelfavatarChanged
|
||||
| EventType::ConfigSynced { .. }
|
||||
| EventType::IncomingMsgBunch { .. }
|
||||
| EventType::ErrorSelfNotInGroup(_) => 0,
|
||||
| EventType::ErrorSelfNotInGroup(_)
|
||||
| EventType::UIChatListChanged => 0,
|
||||
EventType::MsgsChanged { chat_id, .. }
|
||||
| EventType::ReactionsChanged { chat_id, .. }
|
||||
| EventType::IncomingMsg { chat_id, .. }
|
||||
@@ -611,6 +612,9 @@ pub unsafe extern "C" fn dc_event_get_data1_int(event: *mut dc_event_t) -> libc:
|
||||
}
|
||||
EventType::WebxdcStatusUpdate { msg_id, .. } => msg_id.to_u32() as libc::c_int,
|
||||
EventType::WebxdcInstanceDeleted { msg_id, .. } => msg_id.to_u32() as libc::c_int,
|
||||
EventType::UIChatListItemChanged { chat_id } => {
|
||||
chat_id.unwrap_or_default().to_u32() as libc::c_int
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -646,7 +650,8 @@ pub unsafe extern "C" fn dc_event_get_data2_int(event: *mut dc_event_t) -> libc:
|
||||
| EventType::WebxdcInstanceDeleted { .. }
|
||||
| EventType::IncomingMsgBunch { .. }
|
||||
| EventType::SelfavatarChanged
|
||||
| EventType::ConfigSynced { .. } => 0,
|
||||
| EventType::UIChatListChanged
|
||||
| EventType::UIChatListItemChanged { .. } => 0,
|
||||
EventType::ChatModified(_) => 0,
|
||||
EventType::MsgsChanged { msg_id, .. }
|
||||
| EventType::ReactionsChanged { msg_id, .. }
|
||||
@@ -708,7 +713,9 @@ pub unsafe extern "C" fn dc_event_get_data2_str(event: *mut dc_event_t) -> *mut
|
||||
| EventType::SelfavatarChanged
|
||||
| EventType::WebxdcStatusUpdate { .. }
|
||||
| EventType::WebxdcInstanceDeleted { .. }
|
||||
| EventType::ChatEphemeralTimerModified { .. } => ptr::null_mut(),
|
||||
| EventType::ChatEphemeralTimerModified { .. }
|
||||
| EventType::UIChatListItemChanged { .. }
|
||||
| EventType::UIChatListChanged => ptr::null_mut(),
|
||||
EventType::ConfigureProgress { comment, .. } => {
|
||||
if let Some(comment) = comment {
|
||||
comment.to_c_string().unwrap_or_default().into_raw()
|
||||
@@ -725,10 +732,6 @@ pub unsafe extern "C" fn dc_event_get_data2_str(event: *mut dc_event_t) -> *mut
|
||||
.to_c_string()
|
||||
.unwrap_or_default()
|
||||
.into_raw(),
|
||||
EventType::ConfigSynced { key } => {
|
||||
let data2 = key.to_string().to_c_string().unwrap_or_default();
|
||||
data2.into_raw()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat-jsonrpc"
|
||||
version = "1.133.0"
|
||||
version = "1.132.1"
|
||||
description = "DeltaChat JSON-RPC API"
|
||||
edition = "2021"
|
||||
default-run = "deltachat-jsonrpc-server"
|
||||
|
||||
7048
deltachat-jsonrpc/openrpc/openrpc.json
Normal file
7048
deltachat-jsonrpc/openrpc/openrpc.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -28,37 +28,55 @@ pub enum EventType {
|
||||
///
|
||||
/// This event should *not* be reported to the end-user using a popup or something like
|
||||
/// that.
|
||||
Info { msg: String },
|
||||
Info {
|
||||
msg: String,
|
||||
},
|
||||
|
||||
/// Emitted when SMTP connection is established and login was successful.
|
||||
SmtpConnected { msg: String },
|
||||
SmtpConnected {
|
||||
msg: String,
|
||||
},
|
||||
|
||||
/// Emitted when IMAP connection is established and login was successful.
|
||||
ImapConnected { msg: String },
|
||||
ImapConnected {
|
||||
msg: String,
|
||||
},
|
||||
|
||||
/// Emitted when a message was successfully sent to the SMTP server.
|
||||
SmtpMessageSent { msg: String },
|
||||
SmtpMessageSent {
|
||||
msg: String,
|
||||
},
|
||||
|
||||
/// Emitted when an IMAP message has been marked as deleted
|
||||
ImapMessageDeleted { msg: String },
|
||||
ImapMessageDeleted {
|
||||
msg: String,
|
||||
},
|
||||
|
||||
/// Emitted when an IMAP message has been moved
|
||||
ImapMessageMoved { msg: String },
|
||||
ImapMessageMoved {
|
||||
msg: String,
|
||||
},
|
||||
|
||||
/// Emitted before going into IDLE on the Inbox folder.
|
||||
ImapInboxIdle,
|
||||
|
||||
/// Emitted when an new file in the $BLOBDIR was created
|
||||
NewBlobFile { file: String },
|
||||
NewBlobFile {
|
||||
file: String,
|
||||
},
|
||||
|
||||
/// Emitted when an file in the $BLOBDIR was deleted
|
||||
DeletedBlobFile { file: String },
|
||||
DeletedBlobFile {
|
||||
file: String,
|
||||
},
|
||||
|
||||
/// The library-user should write a warning string to the log.
|
||||
///
|
||||
/// This event should *not* be reported to the end-user using a popup or something like
|
||||
/// that.
|
||||
Warning { msg: String },
|
||||
Warning {
|
||||
msg: String,
|
||||
},
|
||||
|
||||
/// The library-user should report an error to the end-user.
|
||||
///
|
||||
@@ -70,14 +88,18 @@ pub enum EventType {
|
||||
/// it might be better to delay showing these events until the function has really
|
||||
/// failed (returned false). It should be sufficient to report only the *last* error
|
||||
/// in a messasge box then.
|
||||
Error { msg: String },
|
||||
Error {
|
||||
msg: String,
|
||||
},
|
||||
|
||||
/// An action cannot be performed because the user is not in the group.
|
||||
/// Reported eg. after a call to
|
||||
/// setChatName(), setChatProfileImage(),
|
||||
/// addContactToChat(), removeContactFromChat(),
|
||||
/// and messages sending functions.
|
||||
ErrorSelfNotInGroup { msg: String },
|
||||
ErrorSelfNotInGroup {
|
||||
msg: String,
|
||||
},
|
||||
|
||||
/// Messages or chats changed. One or more messages or chats changed for various
|
||||
/// reasons in the database:
|
||||
@@ -88,7 +110,10 @@ pub enum EventType {
|
||||
/// `chatId` is set if only a single chat is affected by the changes, otherwise 0.
|
||||
/// `msgId` is set if only a single message is affected by the changes, otherwise 0.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
MsgsChanged { chat_id: u32, msg_id: u32 },
|
||||
MsgsChanged {
|
||||
chat_id: u32,
|
||||
msg_id: u32,
|
||||
},
|
||||
|
||||
/// Reactions for the message changed.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@@ -103,7 +128,10 @@ pub enum EventType {
|
||||
///
|
||||
/// There is no extra #DC_EVENT_MSGS_CHANGED event send together with this event.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
IncomingMsg { chat_id: u32, msg_id: u32 },
|
||||
IncomingMsg {
|
||||
chat_id: u32,
|
||||
msg_id: u32,
|
||||
},
|
||||
|
||||
/// Downloading a bunch of messages just finished. This is an experimental
|
||||
/// event to allow the UI to only show one notification per message bunch,
|
||||
@@ -111,31 +139,47 @@ pub enum EventType {
|
||||
///
|
||||
/// msg_ids contains the message ids.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
IncomingMsgBunch { msg_ids: Vec<u32> },
|
||||
IncomingMsgBunch {
|
||||
msg_ids: Vec<u32>,
|
||||
},
|
||||
|
||||
/// Messages were seen or noticed.
|
||||
/// chat id is always set.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
MsgsNoticed { chat_id: u32 },
|
||||
MsgsNoticed {
|
||||
chat_id: u32,
|
||||
},
|
||||
|
||||
/// A single message is sent successfully. State changed from DC_STATE_OUT_PENDING to
|
||||
/// DC_STATE_OUT_DELIVERED, see `Message.state`.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
MsgDelivered { chat_id: u32, msg_id: u32 },
|
||||
MsgDelivered {
|
||||
chat_id: u32,
|
||||
msg_id: u32,
|
||||
},
|
||||
|
||||
/// A single message could not be sent. State changed from DC_STATE_OUT_PENDING or DC_STATE_OUT_DELIVERED to
|
||||
/// DC_STATE_OUT_FAILED, see `Message.state`.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
MsgFailed { chat_id: u32, msg_id: u32 },
|
||||
MsgFailed {
|
||||
chat_id: u32,
|
||||
msg_id: u32,
|
||||
},
|
||||
|
||||
/// A single message is read by the receiver. State changed from DC_STATE_OUT_DELIVERED to
|
||||
/// DC_STATE_OUT_MDN_RCVD, see `Message.state`.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
MsgRead { chat_id: u32, msg_id: u32 },
|
||||
MsgRead {
|
||||
chat_id: u32,
|
||||
msg_id: u32,
|
||||
},
|
||||
|
||||
/// A single message is deleted.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
MsgDeleted { chat_id: u32, msg_id: u32 },
|
||||
MsgDeleted {
|
||||
chat_id: u32,
|
||||
msg_id: u32,
|
||||
},
|
||||
|
||||
/// Chat changed. The name or the image of a chat group was changed or members were added or removed.
|
||||
/// Or the verify state of a chat has changed.
|
||||
@@ -145,17 +189,24 @@ pub enum EventType {
|
||||
/// This event does not include ephemeral timer modification, which
|
||||
/// is a separate event.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
ChatModified { chat_id: u32 },
|
||||
ChatModified {
|
||||
chat_id: u32,
|
||||
},
|
||||
|
||||
/// Chat ephemeral timer changed.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
ChatEphemeralTimerModified { chat_id: u32, timer: u32 },
|
||||
ChatEphemeralTimerModified {
|
||||
chat_id: u32,
|
||||
timer: u32,
|
||||
},
|
||||
|
||||
/// 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.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
ContactsChanged { contact_id: Option<u32> },
|
||||
ContactsChanged {
|
||||
contact_id: Option<u32>,
|
||||
},
|
||||
|
||||
/// Location of one or more contact has changed.
|
||||
///
|
||||
@@ -163,7 +214,9 @@ pub enum EventType {
|
||||
/// If the locations of several contacts have been changed,
|
||||
/// this parameter is set to `None`.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
LocationChanged { contact_id: Option<u32> },
|
||||
LocationChanged {
|
||||
contact_id: Option<u32>,
|
||||
},
|
||||
|
||||
/// Inform about the configuration progress started by configure().
|
||||
ConfigureProgress {
|
||||
@@ -181,7 +234,9 @@ pub enum EventType {
|
||||
/// @param data1 (usize) 0=error, 1-999=progress in permille, 1000=success and done
|
||||
/// @param data2 0
|
||||
#[serde(rename_all = "camelCase")]
|
||||
ImexProgress { progress: usize },
|
||||
ImexProgress {
|
||||
progress: usize,
|
||||
},
|
||||
|
||||
/// A file has been exported. A file has been written by imex().
|
||||
/// This event may be sent multiple times by a single call to imex().
|
||||
@@ -191,7 +246,9 @@ pub enum EventType {
|
||||
///
|
||||
/// @param data2 0
|
||||
#[serde(rename_all = "camelCase")]
|
||||
ImexFileWritten { path: String },
|
||||
ImexFileWritten {
|
||||
path: String,
|
||||
},
|
||||
|
||||
/// Progress information of a secure-join handshake from the view of the inviter
|
||||
/// (Alice, the person who shows the QR code).
|
||||
@@ -206,7 +263,10 @@ pub enum EventType {
|
||||
/// 800=vg-member-added-received received, shown as "bob@addr securely joined GROUP", only sent for the verified-group-protocol.
|
||||
/// 1000=Protocol finished for this contact.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
SecurejoinInviterProgress { contact_id: u32, progress: usize },
|
||||
SecurejoinInviterProgress {
|
||||
contact_id: u32,
|
||||
progress: usize,
|
||||
},
|
||||
|
||||
/// Progress information of a secure-join handshake from the view of the joiner
|
||||
/// (Bob, the person who scans the QR code).
|
||||
@@ -217,7 +277,10 @@ pub enum EventType {
|
||||
/// 400=vg-/vc-request-with-auth sent, typically shown as "alice@addr verified, introducing myself."
|
||||
/// (Bob has verified alice and waits until Alice does the same for him)
|
||||
#[serde(rename_all = "camelCase")]
|
||||
SecurejoinJoinerProgress { contact_id: u32, progress: usize },
|
||||
SecurejoinJoinerProgress {
|
||||
contact_id: u32,
|
||||
progress: usize,
|
||||
},
|
||||
|
||||
/// The connectivity to the server changed.
|
||||
/// This means that you should refresh the connectivity view
|
||||
@@ -225,17 +288,8 @@ pub enum EventType {
|
||||
/// getConnectivityHtml() for details.
|
||||
ConnectivityChanged,
|
||||
|
||||
/// Deprecated by `ConfigSynced`.
|
||||
SelfavatarChanged,
|
||||
|
||||
/// A multi-device synced config value changed. Maybe the app needs to refresh smth. For
|
||||
/// uniformity this is emitted on the source device too. The value isn't here, otherwise it
|
||||
/// would be logged which might not be good for privacy.
|
||||
ConfigSynced {
|
||||
/// Configuration key.
|
||||
key: String,
|
||||
},
|
||||
|
||||
#[serde(rename_all = "camelCase")]
|
||||
WebxdcStatusUpdate {
|
||||
msg_id: u32,
|
||||
@@ -244,7 +298,21 @@ pub enum EventType {
|
||||
|
||||
/// Inform that a message containing a webxdc instance has been deleted
|
||||
#[serde(rename_all = "camelCase")]
|
||||
WebxdcInstanceDeleted { msg_id: u32 },
|
||||
WebxdcInstanceDeleted {
|
||||
msg_id: u32,
|
||||
},
|
||||
|
||||
/// Inform UI that Order (and content as in chat ids) of the chatlist changed.
|
||||
///
|
||||
/// Sometimes this is emitted together with `UIChatListItemChanged` such as on IncomingMessage.
|
||||
UIChatListChanged,
|
||||
|
||||
/// Inform UI that a single chat list item changed and needs to be rerendered
|
||||
/// If `chat_id` is set to None, then all currently visible chats need to be rerendered, and all not-visible items need to be cleared from cache if the UI has a cache.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
UIChatListItemChanged {
|
||||
chat_id: Option<u32>,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<CoreEventType> for EventType {
|
||||
@@ -340,9 +408,6 @@ impl From<CoreEventType> for EventType {
|
||||
},
|
||||
CoreEventType::ConnectivityChanged => ConnectivityChanged,
|
||||
CoreEventType::SelfavatarChanged => SelfavatarChanged,
|
||||
CoreEventType::ConfigSynced { key } => ConfigSynced {
|
||||
key: key.to_string(),
|
||||
},
|
||||
CoreEventType::WebxdcStatusUpdate {
|
||||
msg_id,
|
||||
status_update_serial,
|
||||
@@ -353,6 +418,10 @@ impl From<CoreEventType> for EventType {
|
||||
CoreEventType::WebxdcInstanceDeleted { msg_id } => WebxdcInstanceDeleted {
|
||||
msg_id: msg_id.to_u32(),
|
||||
},
|
||||
CoreEventType::UIChatListItemChanged { chat_id } => UIChatListItemChanged {
|
||||
chat_id: chat_id.map(|id| id.to_u32()),
|
||||
},
|
||||
CoreEventType::UIChatListChanged => UIChatListChanged,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,5 +53,5 @@
|
||||
},
|
||||
"type": "module",
|
||||
"types": "dist/deltachat.d.ts",
|
||||
"version": "1.133.0"
|
||||
"version": "1.132.1"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat-repl"
|
||||
version = "1.133.0"
|
||||
version = "1.132.1"
|
||||
license = "MPL-2.0"
|
||||
edition = "2021"
|
||||
|
||||
@@ -12,7 +12,7 @@ dirs = "5"
|
||||
log = "0.4.20"
|
||||
pretty_env_logger = "0.5"
|
||||
rusqlite = "0.30"
|
||||
rustyline = "13"
|
||||
rustyline = "12"
|
||||
tokio = { version = "1", features = ["fs", "rt-multi-thread", "macros"] }
|
||||
|
||||
[features]
|
||||
|
||||
@@ -299,8 +299,8 @@ impl Highlighter for DcHelper {
|
||||
self.highlighter.highlight(line, pos)
|
||||
}
|
||||
|
||||
fn highlight_char(&self, line: &str, pos: usize, forced: bool) -> bool {
|
||||
self.highlighter.highlight_char(line, pos, forced)
|
||||
fn highlight_char(&self, line: &str, pos: usize) -> bool {
|
||||
self.highlighter.highlight_char(line, pos)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat-rpc-server"
|
||||
version = "1.133.0"
|
||||
version = "1.132.1"
|
||||
description = "DeltaChat JSON-RPC server"
|
||||
edition = "2021"
|
||||
readme = "README.md"
|
||||
|
||||
@@ -33,6 +33,7 @@ skip = [
|
||||
{ name = "ed25519-dalek", version = "1.0.1" },
|
||||
{ name = "ed25519", version = "1.5.3" },
|
||||
{ name = "event-listener", version = "2.5.3" },
|
||||
{ name = "fd-lock", version = "3.0.13" },
|
||||
{ name = "getrandom", version = "<0.2" },
|
||||
{ name = "h2", version = "0.3.22" },
|
||||
{ name = "http-body", version = "0.4.5" },
|
||||
|
||||
@@ -32,7 +32,6 @@ module.exports = {
|
||||
DC_EVENT_CHAT_EPHEMERAL_TIMER_MODIFIED: 2021,
|
||||
DC_EVENT_CHAT_MODIFIED: 2020,
|
||||
DC_EVENT_CONFIGURE_PROGRESS: 2041,
|
||||
DC_EVENT_CONFIG_SYNCED: 2111,
|
||||
DC_EVENT_CONNECTIVITY_CHANGED: 2100,
|
||||
DC_EVENT_CONTACTS_CHANGED: 2030,
|
||||
DC_EVENT_DELETED_BLOB_FILE: 151,
|
||||
@@ -61,6 +60,8 @@ module.exports = {
|
||||
DC_EVENT_SELFAVATAR_CHANGED: 2110,
|
||||
DC_EVENT_SMTP_CONNECTED: 101,
|
||||
DC_EVENT_SMTP_MESSAGE_SENT: 103,
|
||||
DC_EVENT_UI_CHATLIST_CHANGED: 2200,
|
||||
DC_EVENT_UI_CHATLIST_ITEM_CHANGED: 2201,
|
||||
DC_EVENT_WARNING: 300,
|
||||
DC_EVENT_WEBXDC_INSTANCE_DELETED: 2121,
|
||||
DC_EVENT_WEBXDC_STATUS_UPDATE: 2120,
|
||||
|
||||
@@ -34,7 +34,8 @@ module.exports = {
|
||||
2061: 'DC_EVENT_SECUREJOIN_JOINER_PROGRESS',
|
||||
2100: 'DC_EVENT_CONNECTIVITY_CHANGED',
|
||||
2110: 'DC_EVENT_SELFAVATAR_CHANGED',
|
||||
2111: 'DC_EVENT_CONFIG_SYNCED',
|
||||
2120: 'DC_EVENT_WEBXDC_STATUS_UPDATE',
|
||||
2121: 'DC_EVENT_WEBXDC_INSTANCE_DELETED'
|
||||
2121: 'DC_EVENT_WEBXDC_INSTANCE_DELETED',
|
||||
2200: 'DC_EVENT_UI_CHATLIST_CHANGED',
|
||||
2201: 'DC_EVENT_UI_CHATLIST_ITEM_CHANGED'
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ export enum C {
|
||||
DC_EVENT_CHAT_EPHEMERAL_TIMER_MODIFIED = 2021,
|
||||
DC_EVENT_CHAT_MODIFIED = 2020,
|
||||
DC_EVENT_CONFIGURE_PROGRESS = 2041,
|
||||
DC_EVENT_CONFIG_SYNCED = 2111,
|
||||
DC_EVENT_CONNECTIVITY_CHANGED = 2100,
|
||||
DC_EVENT_CONTACTS_CHANGED = 2030,
|
||||
DC_EVENT_DELETED_BLOB_FILE = 151,
|
||||
@@ -61,6 +60,8 @@ export enum C {
|
||||
DC_EVENT_SELFAVATAR_CHANGED = 2110,
|
||||
DC_EVENT_SMTP_CONNECTED = 101,
|
||||
DC_EVENT_SMTP_MESSAGE_SENT = 103,
|
||||
DC_EVENT_UI_CHATLIST_CHANGED = 2200,
|
||||
DC_EVENT_UI_CHATLIST_ITEM_CHANGED = 2201,
|
||||
DC_EVENT_WARNING = 300,
|
||||
DC_EVENT_WEBXDC_INSTANCE_DELETED = 2121,
|
||||
DC_EVENT_WEBXDC_STATUS_UPDATE = 2120,
|
||||
@@ -320,7 +321,8 @@ export const EventId2EventName: { [key: number]: string } = {
|
||||
2061: 'DC_EVENT_SECUREJOIN_JOINER_PROGRESS',
|
||||
2100: 'DC_EVENT_CONNECTIVITY_CHANGED',
|
||||
2110: 'DC_EVENT_SELFAVATAR_CHANGED',
|
||||
2111: 'DC_EVENT_CONFIG_SYNCED',
|
||||
2120: 'DC_EVENT_WEBXDC_STATUS_UPDATE',
|
||||
2121: 'DC_EVENT_WEBXDC_INSTANCE_DELETED',
|
||||
2200: 'DC_EVENT_UI_CHATLIST_CHANGED',
|
||||
2201: 'DC_EVENT_UI_CHATLIST_ITEM_CHANGED',
|
||||
}
|
||||
|
||||
@@ -56,5 +56,5 @@
|
||||
"test:mocha": "mocha -r esm node/test/test.js --growl --reporter=spec --bail --exit"
|
||||
},
|
||||
"types": "node/dist/index.d.ts",
|
||||
"version": "1.133.0"
|
||||
"version": "1.132.1"
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
2024-01-14
|
||||
2023-12-12
|
||||
@@ -102,7 +102,7 @@ def main():
|
||||
found = True
|
||||
if not found:
|
||||
raise SystemExit(
|
||||
f"CHANGELOG.md contains no entry for version: {newversion}"
|
||||
f"{changelog_name} contains no entry for version: {newversion}"
|
||||
)
|
||||
|
||||
for toml_filename in toml_list:
|
||||
|
||||
28
src/chat.rs
28
src/chat.rs
@@ -46,6 +46,7 @@ use crate::tools::{
|
||||
create_smeared_timestamps, get_abs_path, gm2local_offset, improve_single_line_input,
|
||||
smeared_time, strip_rtlo_characters, time, IsNoneOrEmpty,
|
||||
};
|
||||
use crate::ui_events;
|
||||
use crate::webxdc::WEBXDC_SUFFIX;
|
||||
|
||||
/// An chat item, such as a message or a marker.
|
||||
@@ -309,6 +310,8 @@ impl ChatId {
|
||||
}
|
||||
};
|
||||
context.emit_msgs_changed_without_ids();
|
||||
ui_events::emit_chatlist_changed(context);
|
||||
ui_events::emit_chatlist_item_changed(context, chat_id);
|
||||
Ok(chat_id)
|
||||
}
|
||||
|
||||
@@ -425,6 +428,7 @@ impl ChatId {
|
||||
}
|
||||
}
|
||||
}
|
||||
ui_events::emit_chatlist_changed(context);
|
||||
|
||||
if sync.into() {
|
||||
// NB: For a 1:1 chat this currently triggers `Contact::block()` on other devices.
|
||||
@@ -447,6 +451,8 @@ impl ChatId {
|
||||
pub(crate) async fn unblock_ex(self, context: &Context, sync: sync::Sync) -> Result<()> {
|
||||
self.set_blocked(context, Blocked::Not).await?;
|
||||
|
||||
ui_events::emit_chatlist_changed(context);
|
||||
|
||||
if sync.into() {
|
||||
let chat = Chat::load_from_db(context, self).await?;
|
||||
// TODO: For a 1:1 chat this currently triggers `Contact::unblock()` on other devices.
|
||||
@@ -457,6 +463,7 @@ impl ChatId {
|
||||
.log_err(context)
|
||||
.ok();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -500,6 +507,7 @@ impl ChatId {
|
||||
|
||||
if self.set_blocked(context, Blocked::Not).await? {
|
||||
context.emit_event(EventType::ChatModified(self));
|
||||
ui_events::emit_chatlist_item_changed(context, self);
|
||||
}
|
||||
|
||||
if sync.into() {
|
||||
@@ -542,6 +550,7 @@ impl ChatId {
|
||||
.await?;
|
||||
|
||||
context.emit_event(EventType::ChatModified(self));
|
||||
ui_events::emit_chatlist_item_changed(context, self);
|
||||
|
||||
// make sure, the receivers will get all keys
|
||||
self.reset_gossiped_timestamp(context).await?;
|
||||
@@ -590,6 +599,7 @@ impl ChatId {
|
||||
if protection_status_modified {
|
||||
self.add_protection_msg(context, protect, contact_id, timestamp_sort)
|
||||
.await?;
|
||||
ui_events::emit_chatlist_item_changed(context, self);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -676,6 +686,8 @@ impl ChatId {
|
||||
.await?;
|
||||
|
||||
context.emit_msgs_changed_without_ids();
|
||||
ui_events::emit_chatlist_changed(context);
|
||||
ui_events::emit_chatlist_item_changed(context, self);
|
||||
|
||||
if sync.into() {
|
||||
let chat = Chat::load_from_db(context, self).await?;
|
||||
@@ -782,6 +794,7 @@ impl ChatId {
|
||||
.await?;
|
||||
|
||||
context.emit_msgs_changed_without_ids();
|
||||
ui_events::emit_chatlist_changed(context);
|
||||
|
||||
context.set_config(Config::LastHousekeeping, None).await?;
|
||||
context.scheduler.interrupt_inbox().await;
|
||||
@@ -791,6 +804,7 @@ impl ChatId {
|
||||
msg.text = stock_str::self_deleted_msg_body(context).await;
|
||||
add_device_msg(context, None, Some(&mut msg)).await?;
|
||||
}
|
||||
ui_events::emit_chatlist_changed(context);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -3080,7 +3094,9 @@ pub async fn marknoticed_chat(context: &Context, chat_id: ChatId) -> Result<()>
|
||||
.await?;
|
||||
for chat_id_in_archive in chat_ids_in_archive {
|
||||
context.emit_event(EventType::MsgsNoticed(chat_id_in_archive));
|
||||
ui_events::emit_chatlist_item_changed(context, chat_id_in_archive);
|
||||
}
|
||||
ui_events::emit_chatlist_item_changed(context, DC_CHAT_ID_ARCHIVED_LINK);
|
||||
} else {
|
||||
let exists = context
|
||||
.sql
|
||||
@@ -3107,6 +3123,7 @@ pub async fn marknoticed_chat(context: &Context, chat_id: ChatId) -> Result<()>
|
||||
}
|
||||
|
||||
context.emit_event(EventType::MsgsNoticed(chat_id));
|
||||
ui_events::emit_chatlist_item_changed(context, chat_id);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -3174,6 +3191,7 @@ pub(crate) async fn mark_old_messages_as_noticed(
|
||||
|
||||
for c in changed_chats {
|
||||
context.emit_event(EventType::MsgsNoticed(c));
|
||||
ui_events::emit_chatlist_item_changed(context, c);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -3336,6 +3354,8 @@ pub async fn create_group_chat(
|
||||
}
|
||||
|
||||
context.emit_msgs_changed_without_ids();
|
||||
ui_events::emit_chatlist_changed(context);
|
||||
ui_events::emit_chatlist_item_changed(context, chat_id);
|
||||
|
||||
if protect == ProtectionStatus::Protected {
|
||||
chat_id
|
||||
@@ -3423,11 +3443,14 @@ pub(crate) async fn create_broadcast_list_ex(
|
||||
let chat_id = ChatId::new(u32::try_from(row_id)?);
|
||||
|
||||
context.emit_msgs_changed_without_ids();
|
||||
ui_events::emit_chatlist_changed(context);
|
||||
|
||||
if sync.into() {
|
||||
let id = SyncId::Grpid(grpid);
|
||||
let action = SyncAction::CreateBroadcast(chat_name);
|
||||
self::sync(context, id, action).await.log_err(context).ok();
|
||||
}
|
||||
|
||||
Ok(chat_id)
|
||||
}
|
||||
|
||||
@@ -3698,6 +3721,7 @@ pub(crate) async fn set_muted_ex(
|
||||
.await
|
||||
.context(format!("Failed to set mute duration for {chat_id}"))?;
|
||||
context.emit_event(EventType::ChatModified(chat_id));
|
||||
ui_events::emit_chatlist_item_changed(context, chat_id);
|
||||
if sync.into() {
|
||||
let chat = Chat::load_from_db(context, chat_id).await?;
|
||||
chat.sync(context, SyncAction::SetMuted(duration))
|
||||
@@ -3858,6 +3882,7 @@ async fn rename_ex(
|
||||
sync = Nosync;
|
||||
}
|
||||
context.emit_event(EventType::ChatModified(chat_id));
|
||||
ui_events::emit_chatlist_item_changed(context, chat_id);
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
@@ -3918,6 +3943,7 @@ pub async fn set_chat_profile_image(
|
||||
context.emit_msgs_changed(chat_id, msg.id);
|
||||
}
|
||||
context.emit_event(EventType::ChatModified(chat_id));
|
||||
ui_events::emit_chatlist_item_changed(context, chat_id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -4063,6 +4089,8 @@ pub async fn resend_msgs(context: &Context, msg_ids: &[MsgId]) -> Result<()> {
|
||||
chat_id: msg.chat_id,
|
||||
msg_id: msg.id,
|
||||
});
|
||||
// note(treefit): only matters if it is the last message in chat (but probably to expensive to check, debounce also solves it)
|
||||
ui_events::emit_chatlist_item_changed(context, msg.chat_id);
|
||||
if !create_send_msg_jobs(context, &mut msg).await?.is_empty() {
|
||||
context.scheduler.interrupt_smtp().await;
|
||||
}
|
||||
|
||||
@@ -564,7 +564,6 @@ impl Context {
|
||||
if sync != Sync {
|
||||
return Ok(());
|
||||
}
|
||||
self.emit_event(EventType::ConfigSynced { key });
|
||||
let Some(val) = value else {
|
||||
return Ok(());
|
||||
};
|
||||
@@ -867,43 +866,10 @@ mod tests {
|
||||
// Reset to default. Test that it's not synced because defaults may differ across client
|
||||
// versions.
|
||||
alice0.set_config(Config::MdnsEnabled, None).await?;
|
||||
assert_eq!(alice0.get_config_bool(Config::MdnsEnabled).await?, true);
|
||||
alice0
|
||||
.evtracker
|
||||
.get_matching(|e| {
|
||||
matches!(
|
||||
e,
|
||||
EventType::ConfigSynced {
|
||||
key: Config::MdnsEnabled
|
||||
}
|
||||
)
|
||||
})
|
||||
.await;
|
||||
assert!(alice0.get_config_bool(Config::MdnsEnabled).await?);
|
||||
alice0.set_config_bool(Config::MdnsEnabled, false).await?;
|
||||
alice0
|
||||
.evtracker
|
||||
.get_matching(|e| {
|
||||
matches!(
|
||||
e,
|
||||
EventType::ConfigSynced {
|
||||
key: Config::MdnsEnabled
|
||||
}
|
||||
)
|
||||
})
|
||||
.await;
|
||||
sync(&alice0, &alice1).await;
|
||||
assert_eq!(alice1.get_config_bool(Config::MdnsEnabled).await?, false);
|
||||
alice1
|
||||
.evtracker
|
||||
.get_matching(|e| {
|
||||
matches!(
|
||||
e,
|
||||
EventType::ConfigSynced {
|
||||
key: Config::MdnsEnabled
|
||||
}
|
||||
)
|
||||
})
|
||||
.await;
|
||||
assert!(!alice1.get_config_bool(Config::MdnsEnabled).await?);
|
||||
|
||||
let show_emails = alice0.get_config_bool(Config::ShowEmails).await?;
|
||||
alice0
|
||||
|
||||
@@ -137,11 +137,20 @@ impl ServerParams {
|
||||
}
|
||||
|
||||
fn expand_strict_tls(self) -> Vec<ServerParams> {
|
||||
vec![Self {
|
||||
// Strict if not set by the user or provider database.
|
||||
strict_tls: Some(self.strict_tls.unwrap_or(true)),
|
||||
..self
|
||||
}]
|
||||
if self.strict_tls.is_none() {
|
||||
vec![
|
||||
Self {
|
||||
strict_tls: Some(true), // Strict.
|
||||
..self.clone()
|
||||
},
|
||||
Self {
|
||||
strict_tls: None, // Automatic.
|
||||
..self
|
||||
},
|
||||
]
|
||||
} else {
|
||||
vec![self]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,10 +162,31 @@ pub(crate) fn expand_param_vector(
|
||||
domain: &str,
|
||||
) -> Vec<ServerParams> {
|
||||
v.into_iter()
|
||||
.map(|params| {
|
||||
if params.socket == Socket::Plain {
|
||||
ServerParams {
|
||||
// Avoid expanding plaintext configuration into configuration with and without
|
||||
// `strict_tls` if `strict_tls` is set to `None` as `strict_tls` is not used for
|
||||
// plaintext connections. Always setting it to "enabled", just in case.
|
||||
strict_tls: Some(true),
|
||||
..params
|
||||
}
|
||||
} else {
|
||||
params
|
||||
}
|
||||
})
|
||||
// The order of expansion is important.
|
||||
//
|
||||
// Ports are expanded the last, so they are changed the first. Username is only changed if
|
||||
// default value (address with domain) didn't work for all available hosts and ports.
|
||||
//
|
||||
// Strict TLS must be expanded first, so we try all configurations with strict TLS first
|
||||
// and only then try again without strict TLS. Otherwise we may lock to wrong hostname
|
||||
// without strict TLS when another hostname with strict TLS is available. For example, if
|
||||
// both smtp.example.net and mail.example.net are running an SMTP server, but both use a
|
||||
// certificate that is only valid for mail.example.net, we want to skip smtp.example.net
|
||||
// and use mail.example.net with strict TLS instead of using smtp.example.net without
|
||||
// strict TLS.
|
||||
.flat_map(|params| params.expand_strict_tls().into_iter())
|
||||
.flat_map(|params| params.expand_usernames(addr).into_iter())
|
||||
.flat_map(|params| params.expand_hostnames(domain).into_iter())
|
||||
@@ -227,6 +257,22 @@ mod tests {
|
||||
username: "foobar".to_string(),
|
||||
strict_tls: Some(true)
|
||||
},
|
||||
ServerParams {
|
||||
protocol: Protocol::Smtp,
|
||||
hostname: "example.net".to_string(),
|
||||
port: 123,
|
||||
socket: Socket::Ssl,
|
||||
username: "foobar".to_string(),
|
||||
strict_tls: None,
|
||||
},
|
||||
ServerParams {
|
||||
protocol: Protocol::Smtp,
|
||||
hostname: "example.net".to_string(),
|
||||
port: 123,
|
||||
socket: Socket::Starttls,
|
||||
username: "foobar".to_string(),
|
||||
strict_tls: None
|
||||
}
|
||||
],
|
||||
);
|
||||
|
||||
@@ -238,7 +284,7 @@ mod tests {
|
||||
port: 123,
|
||||
socket: Socket::Plain,
|
||||
username: "foobar".to_string(),
|
||||
strict_tls: Some(true),
|
||||
strict_tls: None,
|
||||
}],
|
||||
"foobar@example.net",
|
||||
"example.net",
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
use deltachat_derive::{FromSql, ToSql};
|
||||
use once_cell::sync::Lazy;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::time::Duration;
|
||||
|
||||
use crate::chat::ChatId;
|
||||
|
||||
@@ -214,6 +215,11 @@ pub(crate) const DC_FOLDERS_CONFIGURED_VERSION: i32 = 4;
|
||||
// `max_smtp_rcpt_to` in the provider db.
|
||||
pub(crate) const DEFAULT_MAX_SMTP_RCPT_TO: usize = 50;
|
||||
|
||||
|
||||
/// How often UI events should be sent out / How much they should be debounced.
|
||||
/// Defines the tick rate/delay of the debounce loop for UI events in milliseconds.
|
||||
pub(crate) const UI_EVENTS_TICK_RATE: Duration = Duration::from_millis(50); // 50ms which means 20 fps
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
@@ -38,7 +38,7 @@ use crate::tools::{
|
||||
duration_to_str, get_abs_path, improve_single_line_input, strip_rtlo_characters, time,
|
||||
EmailAddress,
|
||||
};
|
||||
use crate::{chat, stock_str};
|
||||
use crate::{chat, stock_str, ui_events};
|
||||
|
||||
/// Time during which a contact is considered as seen recently.
|
||||
const SEEN_RECENTLY_SECONDS: i64 = 600;
|
||||
@@ -761,6 +761,7 @@ impl Contact {
|
||||
if count > 0 {
|
||||
// Chat name updated
|
||||
context.emit_event(EventType::ChatModified(chat_id));
|
||||
ui_events::emit_chatlist_items_changed_for_contact(context, contact_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -797,7 +798,31 @@ impl Contact {
|
||||
Ok(row_id)
|
||||
}).await?;
|
||||
|
||||
Ok((ContactId::new(row_id), sth_modified))
|
||||
let contact_id = ContactId::new(row_id);
|
||||
|
||||
Ok((contact_id, sth_modified))
|
||||
}
|
||||
|
||||
/// Get all chats the contact is part of
|
||||
pub async fn get_chats_with_contact(
|
||||
context: &Context,
|
||||
contact_id: &ContactId,
|
||||
) -> Result<Vec<ChatId>> {
|
||||
context
|
||||
.sql
|
||||
.query_map(
|
||||
"SELECT chat_id FROM chats_contacts WHERE contact_id=?",
|
||||
(contact_id,),
|
||||
|row| {
|
||||
let chat_id: ChatId = row.get(0)?;
|
||||
Ok(chat_id)
|
||||
},
|
||||
|rows| {
|
||||
rows.collect::<std::result::Result<Vec<_>, _>>()
|
||||
.map_err(Into::into)
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Add a number of contacts.
|
||||
@@ -1528,6 +1553,7 @@ WHERE type=? AND id IN (
|
||||
}
|
||||
}
|
||||
|
||||
ui_events::emit_chatlist_changed(context);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1576,6 +1602,7 @@ pub(crate) async fn set_profile_image(
|
||||
if changed {
|
||||
contact.update_param(context).await?;
|
||||
context.emit_event(EventType::ContactsChanged(Some(contact_id)));
|
||||
ui_events::emit_chatlist_item_changed_for_contacts_dm_chat(context, contact_id);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -1782,6 +1809,10 @@ impl RecentlySeenLoop {
|
||||
// Timeout, notify about contact.
|
||||
if let Some(contact_id) = contact_id {
|
||||
context.emit_event(EventType::ContactsChanged(Some(*contact_id)));
|
||||
ui_events::emit_chatlist_item_changed_for_contacts_dm_chat(
|
||||
&context,
|
||||
*contact_id,
|
||||
);
|
||||
unseen_queue.pop();
|
||||
}
|
||||
}
|
||||
@@ -1811,6 +1842,10 @@ impl RecentlySeenLoop {
|
||||
// Event is already in the past.
|
||||
if let Some(contact_id) = contact_id {
|
||||
context.emit_event(EventType::ContactsChanged(Some(*contact_id)));
|
||||
ui_events::emit_chatlist_item_changed_for_contacts_dm_chat(
|
||||
&context,
|
||||
*contact_id,
|
||||
);
|
||||
}
|
||||
unseen_queue.pop();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! Context module.
|
||||
|
||||
use std::borrow::BorrowMut;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::ffi::OsString;
|
||||
use std::ops::Deref;
|
||||
@@ -28,6 +29,7 @@ use crate::sql::Sql;
|
||||
use crate::stock_str::StockStrings;
|
||||
use crate::timesmearing::SmearedTimestamp;
|
||||
use crate::tools::{duration_to_str, time};
|
||||
use crate::ui_events::{self, UIEvents};
|
||||
|
||||
/// Builder for the [`Context`].
|
||||
///
|
||||
@@ -203,6 +205,7 @@ pub struct InnerContext {
|
||||
pub(crate) wrong_pw_warning_mutex: Mutex<()>,
|
||||
pub(crate) translated_stockstrings: StockStrings,
|
||||
pub(crate) events: Events,
|
||||
pub(crate) ui_events: Mutex<UIEvents>,
|
||||
|
||||
pub(crate) scheduler: SchedulerState,
|
||||
pub(crate) ratelimit: RwLock<Ratelimit>,
|
||||
@@ -367,6 +370,8 @@ impl Context {
|
||||
// without starting I/O.
|
||||
new_msgs_notify.notify_one();
|
||||
|
||||
let (ui_events, ui_events_receiver) = UIEvents::new();
|
||||
|
||||
let inner = InnerContext {
|
||||
id,
|
||||
blobdir,
|
||||
@@ -378,6 +383,7 @@ impl Context {
|
||||
wrong_pw_warning_mutex: Mutex::new(()),
|
||||
translated_stockstrings: stockstrings,
|
||||
events,
|
||||
ui_events: Mutex::new(ui_events),
|
||||
scheduler: SchedulerState::new(),
|
||||
ratelimit: RwLock::new(Ratelimit::new(Duration::new(60, 0), 6.0)), // Allow at least 1 message every 10 seconds + a burst of 6.
|
||||
quota: RwLock::new(None),
|
||||
@@ -394,6 +400,8 @@ impl Context {
|
||||
inner: Arc::new(inner),
|
||||
};
|
||||
|
||||
ctx.inner.ui_events.blocking_lock().start(&ctx, ui_events_receiver);
|
||||
|
||||
Ok(ctx)
|
||||
}
|
||||
|
||||
@@ -485,11 +493,15 @@ impl Context {
|
||||
/// Emits a MsgsChanged event with specified chat and message ids
|
||||
pub fn emit_msgs_changed(&self, chat_id: ChatId, msg_id: MsgId) {
|
||||
self.emit_event(EventType::MsgsChanged { chat_id, msg_id });
|
||||
ui_events::emit_chatlist_changed(self);
|
||||
ui_events::emit_chatlist_item_changed(self, chat_id);
|
||||
}
|
||||
|
||||
/// Emits an IncomingMsg event with specified chat and message ids
|
||||
pub fn emit_incoming_msg(&self, chat_id: ChatId, msg_id: MsgId) {
|
||||
self.emit_event(EventType::IncomingMsg { chat_id, msg_id });
|
||||
ui_events::emit_chatlist_changed(self);
|
||||
ui_events::emit_chatlist_item_changed(self, chat_id);
|
||||
}
|
||||
|
||||
/// Returns a receiver for emitted events.
|
||||
|
||||
@@ -13,7 +13,7 @@ use crate::imap::{Imap, ImapActionResult};
|
||||
use crate::message::{Message, MsgId, Viewtype};
|
||||
use crate::mimeparser::{MimeMessage, Part};
|
||||
use crate::tools::time;
|
||||
use crate::{stock_str, EventType};
|
||||
use crate::{stock_str, ui_events, EventType};
|
||||
|
||||
/// Download limits should not be used below `MIN_DOWNLOAD_LIMIT`.
|
||||
///
|
||||
@@ -115,6 +115,7 @@ impl MsgId {
|
||||
chat_id: msg.chat_id,
|
||||
msg_id: self,
|
||||
});
|
||||
ui_events::emit_chatlist_item_changed(context, msg.chat_id);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ use async_channel::{self as channel, Receiver, Sender, TrySendError};
|
||||
use pin_project::pin_project;
|
||||
|
||||
mod payload;
|
||||
pub(crate) mod ui_events;
|
||||
|
||||
pub use self::payload::EventType;
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::chat::ChatId;
|
||||
use crate::config::Config;
|
||||
use crate::contact::ContactId;
|
||||
use crate::ephemeral::Timer as EphemeralTimer;
|
||||
use crate::message::MsgId;
|
||||
@@ -262,17 +261,8 @@ pub enum EventType {
|
||||
ConnectivityChanged,
|
||||
|
||||
/// The user's avatar changed.
|
||||
/// Deprecated by `ConfigSynced`.
|
||||
SelfavatarChanged,
|
||||
|
||||
/// A multi-device synced config value changed. Maybe the app needs to refresh smth. For
|
||||
/// uniformity this is emitted on the source device too. The value isn't here, otherwise it
|
||||
/// would be logged which might not be good for privacy.
|
||||
ConfigSynced {
|
||||
/// Configuration key.
|
||||
key: Config,
|
||||
},
|
||||
|
||||
/// Webxdc status update received.
|
||||
WebxdcStatusUpdate {
|
||||
/// Message ID.
|
||||
@@ -287,4 +277,16 @@ pub enum EventType {
|
||||
/// ID of the deleted message.
|
||||
msg_id: MsgId,
|
||||
},
|
||||
|
||||
/// Inform UI that Order (and content as in chat ids) of the chatlist changed.
|
||||
///
|
||||
/// Sometimes this is emitted together with `UIChatListItemChanged` such as on IncomingMessage.
|
||||
UIChatListChanged,
|
||||
|
||||
/// Inform UI that a single chat list item changed and needs to be rerendered
|
||||
/// If `chat_id` is set to None, then all currently visible chats need to be rerendered, and all not-visible items need to be cleared from cache if the UI has a cache.
|
||||
UIChatListItemChanged {
|
||||
/// ID of the changed chat
|
||||
chat_id: Option<ChatId>,
|
||||
},
|
||||
}
|
||||
|
||||
253
src/events/ui_events.rs
Normal file
253
src/events/ui_events.rs
Normal file
@@ -0,0 +1,253 @@
|
||||
use crate::{
|
||||
chat::{ChatId, ChatIdBlocked},
|
||||
constants::UI_EVENTS_TICK_RATE,
|
||||
contact::{Contact, ContactId},
|
||||
context::Context,
|
||||
EventType,
|
||||
};
|
||||
use async_channel::{self as channel, Receiver, Sender};
|
||||
use tokio::{
|
||||
task,
|
||||
time::{sleep_until, Instant},
|
||||
};
|
||||
|
||||
/// order or content of chatlist changes (chat ids, not the actual chatlist item)
|
||||
pub(crate) fn emit_chatlist_changed(context: &Context) {
|
||||
context
|
||||
.ui_events
|
||||
.blocking_lock()
|
||||
.send_chat_list_event(context, InternalUIEvent::ChatListChanged)
|
||||
}
|
||||
|
||||
/// Chatlist item of a specific chat changed
|
||||
pub(crate) fn emit_chatlist_item_changed(context: &Context, chat_id: ChatId) {
|
||||
context
|
||||
.ui_events
|
||||
.blocking_lock()
|
||||
.send_chat_list_event(context, InternalUIEvent::ChatListItemChanged(chat_id))
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
/// Used when you don't know which chatlist items changed, this reloads all cached chatlist items in the UI
|
||||
/// note(treefit): This is not used right now, but I know there will be a point where someone wants it
|
||||
pub(crate) fn emit_unknown_chatlist_items_changed(context: &Context) {
|
||||
context
|
||||
.ui_events
|
||||
.blocking_lock()
|
||||
.send_chat_list_event(context, InternalUIEvent::UnknownChatListItemsChanged)
|
||||
}
|
||||
|
||||
/// update event for dm chat of contact
|
||||
/// used when recently seen changes and when profile image changes
|
||||
pub(crate) fn emit_chatlist_item_changed_for_contacts_dm_chat(
|
||||
context: &Context,
|
||||
contact_id: ContactId,
|
||||
) {
|
||||
context
|
||||
.ui_events
|
||||
.blocking_lock()
|
||||
.send_chat_list_event(context, InternalUIEvent::ContactDMChatChanged(contact_id))
|
||||
}
|
||||
|
||||
/// update dm for chats that have the contact
|
||||
/// used when contact changes their name or did AEAP for example
|
||||
pub(crate) fn emit_chatlist_items_changed_for_contact(context: &Context, contact_id: ContactId) {
|
||||
context
|
||||
.ui_events
|
||||
.blocking_lock()
|
||||
.send_chat_list_event(context, InternalUIEvent::ContactChatsChanged(contact_id));
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum InternalUIEvent {
|
||||
ChatListChanged,
|
||||
ChatListItemChanged(ChatId),
|
||||
UnknownChatListItemsChanged,
|
||||
ContactDMChatChanged(ContactId),
|
||||
ContactChatsChanged(ContactId),
|
||||
}
|
||||
|
||||
struct EventLoopTickState {
|
||||
chat_list_changed: bool,
|
||||
has_unknown_items: bool,
|
||||
chat_ids: Vec<ChatId>,
|
||||
contact_ids_dm: Vec<ContactId>,
|
||||
contact_ids_chats: Vec<ContactId>,
|
||||
}
|
||||
|
||||
impl EventLoopTickState {
|
||||
fn new(capacity: usize) -> Self {
|
||||
Self {
|
||||
chat_list_changed: false,
|
||||
has_unknown_items: false,
|
||||
chat_ids: Vec::with_capacity(capacity),
|
||||
contact_ids_dm: Vec::with_capacity(capacity),
|
||||
contact_ids_chats: Vec::with_capacity(capacity),
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_internal_ui_event(&mut self, event: InternalUIEvent) {
|
||||
match event {
|
||||
InternalUIEvent::ChatListChanged => {
|
||||
self.chat_list_changed = true;
|
||||
}
|
||||
InternalUIEvent::ChatListItemChanged(chat_id) => {
|
||||
self.chat_ids.push(chat_id);
|
||||
}
|
||||
InternalUIEvent::UnknownChatListItemsChanged => {
|
||||
self.has_unknown_items = true;
|
||||
}
|
||||
InternalUIEvent::ContactDMChatChanged(contact_id) => {
|
||||
self.contact_ids_dm.push(contact_id);
|
||||
}
|
||||
InternalUIEvent::ContactChatsChanged(contact_id) => {
|
||||
self.contact_ids_chats.push(contact_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn emit_chatlist_ui_events(&mut self, context: &Context) {
|
||||
if self.chat_list_changed {
|
||||
context.emit_event(EventType::UIChatListChanged);
|
||||
}
|
||||
if self.has_unknown_items {
|
||||
context.emit_event(EventType::UIChatListItemChanged { chat_id: None });
|
||||
return; // since this refreshes everything no further events are needed
|
||||
}
|
||||
|
||||
for contact_id in self
|
||||
.contact_ids_dm
|
||||
.iter()
|
||||
.filter(|contact| !self.contact_ids_chats.contains(contact))
|
||||
.collect::<Vec<&ContactId>>()
|
||||
{
|
||||
if let Ok(Some(chat_id)) = ChatIdBlocked::lookup_by_contact(context, *contact_id).await
|
||||
{
|
||||
self.chat_ids.push(chat_id.id)
|
||||
}
|
||||
}
|
||||
|
||||
// note:(treefit): could make sense to only update chats where the last message is from the contact, but the db query for that is more expensive
|
||||
for contact_id in &self.contact_ids_chats {
|
||||
match Contact::get_chats_with_contact(context, contact_id).await {
|
||||
Ok(contacts_chat_ids) => {
|
||||
self.chat_ids.extend(contacts_chat_ids);
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(
|
||||
context,
|
||||
"Error while getting chats for contact {} in chatlist events loop: {}",
|
||||
contact_id,
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.chat_ids.sort();
|
||||
self.chat_ids.dedup();
|
||||
|
||||
// TODO change event so it accepts a list of chat ids to get rid of this loop? wouldn't work with cffi unless we give it out as json
|
||||
for chat_id in &self.chat_ids {
|
||||
context.emit_event(EventType::UIChatListItemChanged {
|
||||
chat_id: Some(*chat_id),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Debounces UI events
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct UIEvents {
|
||||
task_handle: Option<task::JoinHandle<()>>,
|
||||
chatlist_event_queue: Sender<InternalUIEvent>,
|
||||
}
|
||||
|
||||
impl UIEvents {
|
||||
pub(crate) fn new() -> (Self, Receiver<InternalUIEvent>) {
|
||||
let (chatlist_event_queue, chatlist_event_queue_recv) = channel::unbounded();
|
||||
(
|
||||
Self {
|
||||
task_handle: None,
|
||||
chatlist_event_queue,
|
||||
},
|
||||
chatlist_event_queue_recv,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn start(
|
||||
&mut self,
|
||||
context: &Context,
|
||||
chatlist_event_queue_recv: Receiver<InternalUIEvent>,
|
||||
) {
|
||||
if let Some(handle) = self.task_handle {
|
||||
handle.abort()
|
||||
}
|
||||
self.task_handle = Some(task::spawn(Self::run_task(
|
||||
context,
|
||||
chatlist_event_queue_recv,
|
||||
)))
|
||||
}
|
||||
|
||||
async fn run_task(context: &Context, chatlist_event_queue: Receiver<InternalUIEvent>) {
|
||||
loop {
|
||||
match chatlist_event_queue.recv().await {
|
||||
Ok(chatlist_event) => {
|
||||
let backlog_len = chatlist_event_queue.len();
|
||||
let mut tick_state = EventLoopTickState::new(backlog_len);
|
||||
|
||||
tick_state.apply_internal_ui_event(chatlist_event);
|
||||
// get all events from the queue
|
||||
while let Ok(event) = chatlist_event_queue.try_recv() {
|
||||
tick_state.apply_internal_ui_event(event);
|
||||
}
|
||||
|
||||
tick_state.emit_chatlist_ui_events(context).await;
|
||||
|
||||
// cooldown
|
||||
sleep_until(Instant::now() + UI_EVENTS_TICK_RATE).await;
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(
|
||||
context,
|
||||
"Error receiving an interruption in ui chatlist events loop: {}", err
|
||||
);
|
||||
// Maybe the sender side is closed, so terminate the loop to avoid looping indefinitely.
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn send_chat_list_event(&self, context: &Context, event: InternalUIEvent) {
|
||||
// todo check if ui events are enabled?
|
||||
if let Err(error) = self.chatlist_event_queue.try_send(event) {
|
||||
warn!(
|
||||
context,
|
||||
"Error receiving an interruption in ui chatlist events loop: {}", error
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for UIEvents {
|
||||
fn drop(&mut self) {
|
||||
if let Some(handle) = &self.task_handle {
|
||||
handle.abort()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
// todo tests:
|
||||
|
||||
// send ui events though the UIEventsLoop
|
||||
|
||||
// check that UIEventsLoop really ratelimits the events
|
||||
|
||||
// check that has_unknown_items does not send out any ids before or afterwards
|
||||
|
||||
// if we should make it possible to disable via config then test that as well
|
||||
}
|
||||
@@ -42,6 +42,7 @@ use crate::socks::Socks5Config;
|
||||
use crate::sql;
|
||||
use crate::stock_str;
|
||||
use crate::tools::{create_id, duration_to_str};
|
||||
use crate::ui_events;
|
||||
|
||||
pub(crate) mod capabilities;
|
||||
mod client;
|
||||
@@ -1319,6 +1320,7 @@ impl Imap {
|
||||
.with_context(|| format!("failed to set MODSEQ for folder {folder}"))?;
|
||||
for updated_chat_id in updated_chat_ids {
|
||||
context.emit_event(EventType::MsgsNoticed(updated_chat_id));
|
||||
ui_events::emit_chatlist_item_changed(context, updated_chat_id);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -14,8 +14,8 @@ use crate::context::Context;
|
||||
use crate::events::EventType;
|
||||
use crate::message::{Message, MsgId, Viewtype};
|
||||
use crate::mimeparser::SystemMessage;
|
||||
use crate::stock_str;
|
||||
use crate::tools::{duration_to_str, time};
|
||||
use crate::{stock_str, ui_events};
|
||||
|
||||
/// Location record.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
@@ -290,6 +290,7 @@ pub async fn send_locations_to_chat(
|
||||
chat::add_info_msg(context, chat_id, &stock_str, now).await?;
|
||||
}
|
||||
context.emit_event(EventType::ChatModified(chat_id));
|
||||
ui_events::emit_chatlist_item_changed(context, chat_id);
|
||||
if 0 != seconds {
|
||||
context.scheduler.interrupt_location().await;
|
||||
}
|
||||
@@ -787,6 +788,7 @@ async fn maybe_send_locations(context: &Context) -> Result<Option<u64>> {
|
||||
let stock_str = stock_str::msg_location_disabled(context).await;
|
||||
chat::add_info_msg(context, chat_id, &stock_str, now).await?;
|
||||
context.emit_event(EventType::ChatModified(chat_id));
|
||||
ui_events::emit_chatlist_item_changed(context, chat_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,22 +14,8 @@ use crate::socks::Socks5Config;
|
||||
#[repr(u32)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum CertificateChecks {
|
||||
/// Same as AcceptInvalidCertificates if stored in the database
|
||||
/// as `configured_{imap,smtp}_certificate_checks`.
|
||||
///
|
||||
/// Previous Delta Chat versions stored this in `configured_*`
|
||||
/// if Automatic configuration
|
||||
/// was selected, configuration with strict TLS checks failed
|
||||
/// and configuration without strict TLS checks succeeded.
|
||||
///
|
||||
/// Currently Delta Chat stores only
|
||||
/// `Strict` or `AcceptInvalidCertificates` variants
|
||||
/// in `configured_*` settings.
|
||||
///
|
||||
/// `Automatic` in `{imap,smtp}_certificate_checks`
|
||||
/// means that provider database setting should be taken.
|
||||
/// If there is no provider database setting for certificate checks,
|
||||
/// `Automatic` is the same as `Strict`.
|
||||
/// Same as AcceptInvalidCertificates unless overridden by
|
||||
/// `strict_tls` setting in provider database.
|
||||
Automatic = 0,
|
||||
|
||||
Strict = 1,
|
||||
|
||||
@@ -30,6 +30,7 @@ use crate::tools::{
|
||||
buf_compress, buf_decompress, get_filebytes, get_filemeta, gm2local_offset, read_file, time,
|
||||
timestamp_to_str, truncate,
|
||||
};
|
||||
use crate::ui_events;
|
||||
|
||||
/// Message ID, including reserved IDs.
|
||||
///
|
||||
@@ -138,6 +139,7 @@ WHERE id=?;
|
||||
chat_id,
|
||||
msg_id: self,
|
||||
});
|
||||
ui_events::emit_chatlist_item_changed(context, chat_id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1518,9 +1520,12 @@ pub async fn delete_msgs(context: &Context, msg_ids: &[MsgId]) -> Result<()> {
|
||||
|
||||
for modified_chat_id in modified_chat_ids {
|
||||
context.emit_msgs_changed(modified_chat_id, MsgId::new(0));
|
||||
ui_events::emit_chatlist_item_changed(context, modified_chat_id);
|
||||
}
|
||||
|
||||
if !msg_ids.is_empty() {
|
||||
context.emit_msgs_changed_without_ids();
|
||||
ui_events::emit_chatlist_changed(context);
|
||||
// Run housekeeping to delete unused blobs.
|
||||
context.set_config(Config::LastHousekeeping, None).await?;
|
||||
}
|
||||
@@ -1653,6 +1658,7 @@ pub async fn markseen_msgs(context: &Context, msg_ids: Vec<MsgId>) -> Result<()>
|
||||
|
||||
for updated_chat_id in updated_chat_ids {
|
||||
context.emit_event(EventType::MsgsNoticed(updated_chat_id));
|
||||
ui_events::emit_chatlist_item_changed(context, updated_chat_id);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -1713,6 +1719,7 @@ pub(crate) async fn set_msg_failed(
|
||||
chat_id: msg.chat_id,
|
||||
msg_id: msg.id,
|
||||
});
|
||||
ui_events::emit_chatlist_item_changed(context, msg.chat_id);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -351,7 +351,7 @@ impl<'a> MimeFactory<'a> {
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
Loaded::Mdn { .. } => false,
|
||||
Loaded::Mdn { .. } => true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1571,7 +1571,6 @@ mod tests {
|
||||
use crate::mimeparser::MimeMessage;
|
||||
use crate::receive_imf::receive_imf;
|
||||
use crate::test_utils::{get_chat_msg, TestContext, TestContextManager};
|
||||
|
||||
#[test]
|
||||
fn test_render_email_address() {
|
||||
let display_name = "ä space";
|
||||
@@ -1827,37 +1826,6 @@ mod tests {
|
||||
assert_eq!("Re: Hello, Bob", mf.subject_str(&t).await.unwrap());
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mdn_create_encrypted() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = tcm.alice().await;
|
||||
let bob = tcm.bob().await;
|
||||
bob.set_config_bool(Config::MdnsEnabled, true).await?;
|
||||
|
||||
let mut msg = Message::new(Viewtype::Text);
|
||||
msg.param.set_int(Param::SkipAutocrypt, 1);
|
||||
let chat_alice = alice.create_chat(&bob).await.id;
|
||||
let sent = alice.send_msg(chat_alice, &mut msg).await;
|
||||
|
||||
let rcvd = bob.recv_msg(&sent).await;
|
||||
message::markseen_msgs(&bob, vec![rcvd.id]).await?;
|
||||
let mimefactory = MimeFactory::from_mdn(&bob, &rcvd, vec![]).await?;
|
||||
let rendered_msg = mimefactory.render(&bob).await?;
|
||||
|
||||
assert!(!rendered_msg.is_encrypted);
|
||||
|
||||
let rcvd = tcm.send_recv(&alice, &bob, "Heyho").await;
|
||||
message::markseen_msgs(&bob, vec![rcvd.id]).await?;
|
||||
|
||||
let mimefactory = MimeFactory::from_mdn(&bob, &rcvd, vec![]).await?;
|
||||
let rendered_msg = mimefactory.render(&bob).await?;
|
||||
|
||||
// When encrypted, the MDN should be encrypted as well
|
||||
assert!(rendered_msg.is_encrypted);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_subject_in_group() -> Result<()> {
|
||||
async fn send_msg_get_subject(
|
||||
|
||||
@@ -35,13 +35,12 @@ use crate::message::{
|
||||
use crate::param::{Param, Params};
|
||||
use crate::peerstate::Peerstate;
|
||||
use crate::simplify::{simplify, SimplifiedText};
|
||||
use crate::stock_str;
|
||||
use crate::sync::SyncItems;
|
||||
use crate::tools::{
|
||||
create_smeared_timestamp, get_filemeta, parse_receive_headers, smeared_time,
|
||||
strip_rtlo_characters, truncate_by_lines,
|
||||
};
|
||||
use crate::{location, tools};
|
||||
use crate::{location, stock_str, tools, ui_events};
|
||||
|
||||
/// A parsed MIME message.
|
||||
///
|
||||
@@ -2119,6 +2118,8 @@ async fn handle_mdn(
|
||||
{
|
||||
update_msg_state(context, msg_id, MessageState::OutMdnRcvd).await?;
|
||||
context.emit_event(EventType::MsgRead { chat_id, msg_id });
|
||||
// note(treefit): only matters if it is the last message in chat (but probably to expensive to check, debounce also solves it)
|
||||
ui_events::emit_chatlist_item_changed(context, chat_id);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ use crate::key::{DcKey, Fingerprint, SignedPublicKey};
|
||||
use crate::message::Message;
|
||||
use crate::mimeparser::SystemMessage;
|
||||
use crate::sql::Sql;
|
||||
use crate::stock_str;
|
||||
use crate::{stock_str, ui_events};
|
||||
|
||||
/// Type of the public key stored inside the peerstate.
|
||||
#[derive(Debug)]
|
||||
@@ -695,6 +695,9 @@ impl Peerstate {
|
||||
.await?;
|
||||
}
|
||||
|
||||
ui_events::emit_chatlist_changed(context);
|
||||
// update the chats the contact is part of
|
||||
ui_events::emit_chatlist_items_changed_for_contact(context, contact_id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ use crate::ephemeral::{stock_ephemeral_timer_changed, Timer as EphemeralTimer};
|
||||
use crate::events::EventType;
|
||||
use crate::headerdef::{HeaderDef, HeaderDefMap};
|
||||
use crate::imap::{markseen_on_imap_table, GENERATED_PREFIX};
|
||||
use crate::location;
|
||||
use crate::log::LogExt;
|
||||
use crate::message::{
|
||||
self, rfc724_mid_exists, rfc724_mid_exists_and, Message, MessageState, MessengerMessage, MsgId,
|
||||
@@ -40,6 +39,7 @@ use crate::stock_str;
|
||||
use crate::sync::Sync::*;
|
||||
use crate::tools::{buf_compress, extract_grpid_from_rfc724_mid, strip_rtlo_characters};
|
||||
use crate::{contact, imap};
|
||||
use crate::{location, ui_events};
|
||||
|
||||
/// This is the struct that is returned after receiving one email (aka MIME message).
|
||||
///
|
||||
@@ -1778,6 +1778,8 @@ async fn create_or_lookup_group(
|
||||
chat::add_to_chat_contacts_table(context, new_chat_id, &members).await?;
|
||||
|
||||
context.emit_event(EventType::ChatModified(new_chat_id));
|
||||
ui_events::emit_chatlist_changed(context);
|
||||
ui_events::emit_chatlist_item_changed(context, new_chat_id);
|
||||
}
|
||||
|
||||
if let Some(chat_id) = chat_id {
|
||||
@@ -2069,6 +2071,7 @@ async fn apply_group_changes(
|
||||
|
||||
if send_event_chat_modified {
|
||||
context.emit_event(EventType::ChatModified(chat_id));
|
||||
ui_events::emit_chatlist_item_changed(context, chat_id);
|
||||
}
|
||||
Ok((group_changes_msgs, better_msg))
|
||||
}
|
||||
@@ -2368,6 +2371,8 @@ async fn create_adhoc_group(
|
||||
chat::add_to_chat_contacts_table(context, new_chat_id, member_ids).await?;
|
||||
|
||||
context.emit_event(EventType::ChatModified(new_chat_id));
|
||||
ui_events::emit_chatlist_changed(context);
|
||||
ui_events::emit_chatlist_item_changed(context, new_chat_id);
|
||||
|
||||
Ok(Some(new_chat_id))
|
||||
}
|
||||
|
||||
@@ -70,11 +70,8 @@ impl SchedulerState {
|
||||
context.new_msgs_notify.notify_one();
|
||||
|
||||
let ctx = context.clone();
|
||||
match Scheduler::start(&context).await {
|
||||
Ok(scheduler) => {
|
||||
*inner = InnerSchedulerState::Started(scheduler);
|
||||
context.emit_event(EventType::ConnectivityChanged);
|
||||
}
|
||||
match Scheduler::start(context).await {
|
||||
Ok(scheduler) => *inner = InnerSchedulerState::Started(scheduler),
|
||||
Err(err) => error!(&ctx, "Failed to start IO: {:#}", err),
|
||||
}
|
||||
}
|
||||
@@ -119,7 +116,6 @@ impl SchedulerState {
|
||||
debug_logging.loop_handle.abort();
|
||||
}
|
||||
let prev_state = std::mem::replace(&mut *inner, new_state);
|
||||
context.emit_event(EventType::ConnectivityChanged);
|
||||
match prev_state {
|
||||
InnerSchedulerState::Started(scheduler) => scheduler.stop(context).await,
|
||||
InnerSchedulerState::Stopped | InnerSchedulerState::Paused { .. } => (),
|
||||
@@ -776,7 +772,7 @@ async fn smtp_loop(
|
||||
|
||||
impl Scheduler {
|
||||
/// Start the scheduler.
|
||||
pub async fn start(ctx: &Context) -> Result<Self> {
|
||||
pub async fn start(ctx: Context) -> Result<Self> {
|
||||
let (smtp, smtp_handlers) = SmtpConnectionState::new();
|
||||
|
||||
let (smtp_start_send, smtp_start_recv) = oneshot::channel();
|
||||
@@ -786,7 +782,7 @@ impl Scheduler {
|
||||
let mut oboxes = Vec::new();
|
||||
let mut start_recvs = Vec::new();
|
||||
|
||||
let (conn_state, inbox_handlers) = ImapConnectionState::new(ctx).await?;
|
||||
let (conn_state, inbox_handlers) = ImapConnectionState::new(&ctx).await?;
|
||||
let (inbox_start_send, inbox_start_recv) = oneshot::channel();
|
||||
let handle = {
|
||||
let ctx = ctx.clone();
|
||||
@@ -807,7 +803,7 @@ impl Scheduler {
|
||||
),
|
||||
] {
|
||||
if should_watch? {
|
||||
let (conn_state, handlers) = ImapConnectionState::new(ctx).await?;
|
||||
let (conn_state, handlers) = ImapConnectionState::new(&ctx).await?;
|
||||
let (start_send, start_recv) = oneshot::channel();
|
||||
let ctx = ctx.clone();
|
||||
let handle = task::spawn(simple_imap_loop(ctx, start_send, handlers, meaning));
|
||||
|
||||
@@ -25,6 +25,7 @@ use crate::stock_str;
|
||||
use crate::sync::Sync::*;
|
||||
use crate::token;
|
||||
use crate::tools::time;
|
||||
use crate::ui_events;
|
||||
|
||||
mod bob;
|
||||
mod bobstate;
|
||||
@@ -683,6 +684,7 @@ async fn secure_connection_established(
|
||||
)
|
||||
.await?;
|
||||
context.emit_event(EventType::ChatModified(chat_id));
|
||||
ui_events::emit_chatlist_item_changed(context, chat_id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -618,7 +618,7 @@ async fn send_mdns(context: &Context, connection: &mut Smtp) -> Result<()> {
|
||||
|
||||
let more_mdns = send_mdn(context, connection).await?;
|
||||
if !more_mdns {
|
||||
// No more MDNs to send or one of them failed.
|
||||
// No more MDNs to send.
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
@@ -752,7 +752,7 @@ async fn send_mdn_msg_id(
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to send a single MDN. Returns true if more MDNs should be sent.
|
||||
/// Tries to send a single MDN. Returns false if there are no MDNs to send.
|
||||
async fn send_mdn(context: &Context, smtp: &mut Smtp) -> Result<bool> {
|
||||
let mdns_enabled = context.get_config_bool(Config::MdnsEnabled).await?;
|
||||
if !mdns_enabled {
|
||||
|
||||
@@ -17,7 +17,7 @@ Seen status synchronization | IMAP CONDSTORE extension ([RFC 7162][])
|
||||
Client/server identification | IMAP ID extension ([RFC 2971][])
|
||||
Authorization | OAuth2 ([RFC 6749][])
|
||||
End-to-end encryption | [Autocrypt Level 1][], OpenPGP ([RFC 4880][]), Security Multiparts for MIME ([RFC 1847][]) and [“Mixed Up” Encryption repairing](https://tools.ietf.org/id/draft-dkg-openpgp-pgpmime-message-mangling-00.html)
|
||||
Detect/prevent active attacks | [securejoin][] protocols
|
||||
Detect/prevent active attacks | [countermitm][] protocols
|
||||
Compare public keys | [openpgp4fpr][] URI Scheme
|
||||
Header encryption | [Protected Headers for Cryptographic E-mail](https://datatracker.ietf.org/doc/draft-autocrypt-lamps-protected-headers/)
|
||||
Configuration assistance | [Autoconfigure](https://web.archive.org/web/20210402044801/https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration) and [Autodiscover][]
|
||||
@@ -29,7 +29,7 @@ Return receipts | Message Disposition Notification (MDN, [RFC 8
|
||||
Locations | KML ([Open Geospatial Consortium](http://www.opengeospatial.org/standards/kml/), [Google Dev](https://developers.google.com/kml/))
|
||||
|
||||
[Autocrypt Level 1]: https://autocrypt.org/level1.html
|
||||
[securejoin]: https://securejoin.readthedocs.io/en/latest/
|
||||
[countermitm]: https://countermitm.readthedocs.io/en/latest/
|
||||
[openpgp4fpr]: https://metacode.biz/openpgp/openpgp4fpr
|
||||
[Autodiscover]: https://learn.microsoft.com/en-us/exchange/autodiscover-service-for-exchange-2013
|
||||
[XEP-0392]: https://xmpp.org/extensions/xep-0392.html
|
||||
|
||||
Reference in New Issue
Block a user