mirror of
https://github.com/chatmail/core.git
synced 2026-04-20 23:16:30 +03:00
Connectivity view (instead of spamming the user with error_network when sth fails) (#2319)
See https://support.delta.chat/t/discussion-how-to-show-error-states/1363/10 <!-- comment --> It turns out that it's pretty easy to distinguish between lots of states (currently Error/NotConnected, Connecting…, Getting new messages… and Connected). What's not that easy is distinguishing between an actual error and no network, because if the server just doesn't respond, it could mean that we don't have network or that we are trying ipv6, but only ipv4 works. **WRT debouncing:** Sending of EVENT_CONNECTIVITY_CHANGED is not debounced, but emitted every time one of the 3 threads (Inbox, Mvbox and Sentbox) has a network error, starts fetching data, or is done fetching data. This means that it is emitted: - 9 times when dc_maybe_network() is called or we get network connection - 12 times when we lose network connection Some measurements: dc_get_connectivity() takes a little more than 1ms (in my measurements back in March), dc_get_connectivity_html() takes 10-20ms. This means that it's no immmediate problem to call them very often, might increase battery drain though. For the UI it may be a lot of work to update the title everytime; at least Android is smart enough to update the title only once. Possible problems (we don't have to worry about them now I think): - Due to the scan_folders feature, if the user has lots of folders, the state could be "Connecting..." for quite a long time, generally DC seemed a little unresponsive to me because it took so long for "Connecting..." to go away. Telegram has a state "Updating..." that sometimes comes after "Connecting...". To be done in other PRs: - Better handle the case that the password was changed on the server and authenticating fails, see https://github.com/deltachat/deltachat-core-rust/issues/1923 and https://github.com/deltachat/deltachat-core-rust/issues/1768 - maybe event debouncing (except for "Connected" connectivity events) fix https://github.com/deltachat/deltachat-android/issues/1760
This commit is contained in:
@@ -13,6 +13,10 @@ use crate::job::{self, Thread};
|
||||
use crate::message::MsgId;
|
||||
use crate::smtp::Smtp;
|
||||
|
||||
use self::connectivity::ConnectivityStore;
|
||||
|
||||
pub(crate) mod connectivity;
|
||||
|
||||
pub(crate) struct StopToken;
|
||||
|
||||
/// Job and connection scheduler.
|
||||
@@ -35,7 +39,9 @@ pub(crate) enum Scheduler {
|
||||
impl Context {
|
||||
/// Indicate that the network likely has come back.
|
||||
pub async fn maybe_network(&self) {
|
||||
self.scheduler.read().await.maybe_network().await;
|
||||
let lock = self.scheduler.read().await;
|
||||
lock.maybe_network().await;
|
||||
connectivity::idle_interrupted(lock).await;
|
||||
}
|
||||
|
||||
pub(crate) async fn interrupt_inbox(&self, info: InterruptInfo) {
|
||||
@@ -107,6 +113,9 @@ async fn inbox_loop(ctx: Context, started: Sender<()>, inbox_handlers: ImapConne
|
||||
} else {
|
||||
if let Err(err) = connection.scan_folders(&ctx).await {
|
||||
warn!(ctx, "{}", err);
|
||||
connection.connectivity.set_err(&ctx, err).await;
|
||||
} else {
|
||||
connection.connectivity.set_not_configured(&ctx).await;
|
||||
}
|
||||
connection.fake_idle(&ctx, None).await
|
||||
};
|
||||
@@ -132,26 +141,24 @@ async fn fetch(ctx: &Context, connection: &mut Imap) {
|
||||
match ctx.get_config(Config::ConfiguredInboxFolder).await {
|
||||
Ok(Some(watch_folder)) => {
|
||||
if let Err(err) = connection.prepare(ctx).await {
|
||||
error_network!(ctx, "{}", err);
|
||||
warn!(ctx, "Could not connect: {}", err);
|
||||
return;
|
||||
}
|
||||
|
||||
// fetch
|
||||
if let Err(err) = connection.fetch(ctx, &watch_folder).await {
|
||||
connection.trigger_reconnect();
|
||||
connection.trigger_reconnect(ctx).await;
|
||||
warn!(ctx, "{:#}", err);
|
||||
}
|
||||
}
|
||||
Ok(None) => {
|
||||
warn!(ctx, "Can not fetch inbox folder, not set");
|
||||
connection.fake_idle(ctx, None).await;
|
||||
info!(ctx, "Can not fetch inbox folder, not set");
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(
|
||||
ctx,
|
||||
"Can not fetch inbox folder, failed to get config: {:?}", err
|
||||
);
|
||||
connection.fake_idle(ctx, None).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -167,8 +174,9 @@ async fn fetch_idle(ctx: &Context, connection: &mut Imap, folder: Config) -> Int
|
||||
|
||||
// fetch
|
||||
if let Err(err) = connection.fetch(ctx, &watch_folder).await {
|
||||
connection.trigger_reconnect();
|
||||
connection.trigger_reconnect(ctx).await;
|
||||
warn!(ctx, "{:#}", err);
|
||||
return InterruptInfo::new(false, None);
|
||||
}
|
||||
|
||||
if folder == Config::ConfiguredInboxFolder {
|
||||
@@ -180,22 +188,25 @@ async fn fetch_idle(ctx: &Context, connection: &mut Imap, folder: Config) -> Int
|
||||
}
|
||||
}
|
||||
|
||||
connection.connectivity.set_connected(ctx).await;
|
||||
|
||||
// idle
|
||||
if connection.can_idle() {
|
||||
connection
|
||||
.idle(ctx, Some(watch_folder))
|
||||
.await
|
||||
.unwrap_or_else(|err| {
|
||||
connection.trigger_reconnect();
|
||||
match connection.idle(ctx, Some(watch_folder)).await {
|
||||
Ok(v) => v,
|
||||
Err(err) => {
|
||||
connection.trigger_reconnect(ctx).await;
|
||||
warn!(ctx, "{}", err);
|
||||
InterruptInfo::new(false, None)
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
connection.fake_idle(ctx, Some(watch_folder)).await
|
||||
}
|
||||
}
|
||||
Ok(None) => {
|
||||
warn!(ctx, "Can not watch {} folder, not set", folder);
|
||||
connection.connectivity.set_not_configured(ctx).await;
|
||||
info!(ctx, "Can not watch {} folder, not set", folder);
|
||||
connection.fake_idle(ctx, None).await
|
||||
}
|
||||
Err(err) => {
|
||||
@@ -280,6 +291,7 @@ async fn smtp_loop(ctx: Context, started: Sender<()>, smtp_handlers: SmtpConnect
|
||||
None => {
|
||||
// Fake Idle
|
||||
info!(ctx, "smtp fake idle - started");
|
||||
connection.connectivity.set_connected(&ctx).await;
|
||||
interrupt_info = idle_interrupt_receiver.recv().await.unwrap_or_default();
|
||||
info!(ctx, "smtp fake idle - interrupted")
|
||||
}
|
||||
@@ -338,6 +350,11 @@ impl Scheduler {
|
||||
.send(())
|
||||
.await
|
||||
.expect("mvbox start send, missing receiver");
|
||||
mvbox_handlers
|
||||
.connection
|
||||
.connectivity
|
||||
.set_not_configured(&ctx)
|
||||
.await
|
||||
}
|
||||
|
||||
if ctx.get_config_bool(Config::SentboxWatch).await? {
|
||||
@@ -356,6 +373,11 @@ impl Scheduler {
|
||||
.send(())
|
||||
.await
|
||||
.expect("sentbox start send, missing receiver");
|
||||
sentbox_handlers
|
||||
.connection
|
||||
.connectivity
|
||||
.set_not_configured(&ctx)
|
||||
.await
|
||||
}
|
||||
|
||||
let smtp_handle = {
|
||||
@@ -508,6 +530,8 @@ struct ConnectionState {
|
||||
stop_sender: Sender<()>,
|
||||
/// Channel to interrupt idle.
|
||||
idle_interrupt_sender: Sender<InterruptInfo>,
|
||||
/// Mutex to pass connectivity info between IMAP/SMTP threads and the API
|
||||
connectivity: ConnectivityStore,
|
||||
}
|
||||
|
||||
impl ConnectionState {
|
||||
@@ -550,6 +574,7 @@ impl SmtpConnectionState {
|
||||
shutdown_receiver,
|
||||
stop_sender,
|
||||
idle_interrupt_sender,
|
||||
connectivity: handlers.connection.connectivity.clone(),
|
||||
};
|
||||
|
||||
let conn = SmtpConnectionState { state };
|
||||
@@ -597,6 +622,7 @@ impl ImapConnectionState {
|
||||
shutdown_receiver,
|
||||
stop_sender,
|
||||
idle_interrupt_sender,
|
||||
connectivity: handlers.connection.connectivity.clone(),
|
||||
};
|
||||
|
||||
let conn = ImapConnectionState { state };
|
||||
|
||||
Reference in New Issue
Block a user