mirror of
https://github.com/chatmail/core.git
synced 2026-05-08 09:26:29 +03:00
feat: connectivity view: move quota up and combine with IMAP state. (#7653)
like described in https://github.com/chatmail/core/pull/7630#discussion_r2641514867 ## classical account (with legacy option mvbox) ||| |---|---| | <img width="891" height="635" alt="image" src="https://github.com/user-attachments/assets/723f3dba-79dc-4b57-a14f-c5879c1a1d1d" />| <img width="890" height="578" alt="image" src="https://github.com/user-attachments/assets/d45eaf35-d7b2-40d4-8c37-bbc77947c27d" />| ## multi transport ||| |---|---| |<img width="891" height="1236" alt="image" src="https://github.com/user-attachments/assets/053cb088-7d9d-4591-b2bc-6b49399d33a0" /> |<img width="885" height="1230" alt="image" src="https://github.com/user-attachments/assets/c455b4f1-f521-4ae8-8884-9042af62ca46" />|
This commit is contained in:
@@ -324,8 +324,8 @@ impl Drop for IoPausedGuard {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct SchedBox {
|
struct SchedBox {
|
||||||
/// Hostname of used chatmail/email relay
|
/// Address at the used chatmail/email relay
|
||||||
host: String,
|
addr: String,
|
||||||
meaning: FolderMeaning,
|
meaning: FolderMeaning,
|
||||||
conn_state: ImapConnectionState,
|
conn_state: ImapConnectionState,
|
||||||
|
|
||||||
@@ -831,14 +831,9 @@ impl Scheduler {
|
|||||||
let ctx = ctx.clone();
|
let ctx = ctx.clone();
|
||||||
task::spawn(inbox_loop(ctx, inbox_start_send, inbox_handlers))
|
task::spawn(inbox_loop(ctx, inbox_start_send, inbox_handlers))
|
||||||
};
|
};
|
||||||
let host = configured_login_param
|
let addr = configured_login_param.addr.clone();
|
||||||
.addr
|
|
||||||
.split("@")
|
|
||||||
.last()
|
|
||||||
.context("address has no host")?
|
|
||||||
.to_owned();
|
|
||||||
let inbox = SchedBox {
|
let inbox = SchedBox {
|
||||||
host: host.clone(),
|
addr: addr.clone(),
|
||||||
meaning: FolderMeaning::Inbox,
|
meaning: FolderMeaning::Inbox,
|
||||||
conn_state,
|
conn_state,
|
||||||
handle,
|
handle,
|
||||||
@@ -854,7 +849,7 @@ impl Scheduler {
|
|||||||
let meaning = FolderMeaning::Mvbox;
|
let meaning = FolderMeaning::Mvbox;
|
||||||
let handle = task::spawn(simple_imap_loop(ctx, start_send, handlers, meaning));
|
let handle = task::spawn(simple_imap_loop(ctx, start_send, handlers, meaning));
|
||||||
oboxes.push(SchedBox {
|
oboxes.push(SchedBox {
|
||||||
host,
|
addr,
|
||||||
meaning,
|
meaning,
|
||||||
conn_state,
|
conn_state,
|
||||||
handle,
|
handle,
|
||||||
|
|||||||
@@ -343,9 +343,18 @@ impl Context {
|
|||||||
.green {
|
.green {
|
||||||
background-color: #34c759;
|
background-color: #34c759;
|
||||||
}
|
}
|
||||||
|
.grey {
|
||||||
|
background-color: #808080;
|
||||||
|
}
|
||||||
.yellow {
|
.yellow {
|
||||||
background-color: #fdc625;
|
background-color: #fdc625;
|
||||||
}
|
}
|
||||||
|
.transport {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
.quota-list {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>"#
|
<body>"#
|
||||||
@@ -375,7 +384,7 @@ impl Context {
|
|||||||
.boxes()
|
.boxes()
|
||||||
.map(|b| {
|
.map(|b| {
|
||||||
(
|
(
|
||||||
b.host.clone(),
|
b.addr.clone(),
|
||||||
b.meaning,
|
b.meaning,
|
||||||
b.conn_state.state.connectivity.clone(),
|
b.conn_state.state.connectivity.clone(),
|
||||||
)
|
)
|
||||||
@@ -396,73 +405,15 @@ impl Context {
|
|||||||
// =============================================================================================
|
// =============================================================================================
|
||||||
// Add e.g.
|
// Add e.g.
|
||||||
// Incoming messages
|
// Incoming messages
|
||||||
// - "Inbox": Connected
|
// - [X] nine.testrun.org: Connected
|
||||||
|
// 1.34 GiB of 2 GiB used
|
||||||
|
// [======67%===== ]
|
||||||
// =============================================================================================
|
// =============================================================================================
|
||||||
|
|
||||||
let watched_folders = get_watched_folder_configs(self).await?;
|
let watched_folders = get_watched_folder_configs(self).await?;
|
||||||
let incoming_messages = stock_str::incoming_messages(self).await;
|
let incoming_messages = stock_str::incoming_messages(self).await;
|
||||||
ret += &format!("<h3>{incoming_messages}</h3><ul>");
|
ret += &format!("<h3>{incoming_messages}</h3><ul>");
|
||||||
for (host, folder, state) in &folders_states {
|
|
||||||
let mut folder_added = false;
|
|
||||||
|
|
||||||
if let Some(config) = folder.to_config().filter(|c| watched_folders.contains(c)) {
|
|
||||||
let f = self.get_config(config).await.log_err(self).ok().flatten();
|
|
||||||
|
|
||||||
if let Some(foldername) = f {
|
|
||||||
let detailed = &state.get_detailed();
|
|
||||||
ret += "<li>";
|
|
||||||
ret += &*detailed.to_icon();
|
|
||||||
ret += " <b>";
|
|
||||||
if folder == &FolderMeaning::Inbox {
|
|
||||||
ret += &*escaper::encode_minimal(host);
|
|
||||||
} else {
|
|
||||||
ret += &*escaper::encode_minimal(&foldername);
|
|
||||||
}
|
|
||||||
ret += ":</b> ";
|
|
||||||
ret += &*escaper::encode_minimal(&detailed.to_string_imap(self).await);
|
|
||||||
ret += "</li>";
|
|
||||||
|
|
||||||
folder_added = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !folder_added && folder == &FolderMeaning::Inbox {
|
|
||||||
let detailed = &state.get_detailed();
|
|
||||||
if let DetailedConnectivity::Error(_) = detailed {
|
|
||||||
// On the inbox thread, we also do some other things like scan_folders and run jobs
|
|
||||||
// so, maybe, the inbox is not watched, but something else went wrong
|
|
||||||
ret += "<li>";
|
|
||||||
ret += &*detailed.to_icon();
|
|
||||||
ret += " ";
|
|
||||||
ret += &*escaper::encode_minimal(&detailed.to_string_imap(self).await);
|
|
||||||
ret += "</li>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret += "</ul>";
|
|
||||||
|
|
||||||
// =============================================================================================
|
|
||||||
// Add e.g.
|
|
||||||
// Outgoing messages
|
|
||||||
// Your last message was sent successfully
|
|
||||||
// =============================================================================================
|
|
||||||
|
|
||||||
let outgoing_messages = stock_str::outgoing_messages(self).await;
|
|
||||||
ret += &format!("<h3>{outgoing_messages}</h3><ul><li>");
|
|
||||||
let detailed = smtp.get_detailed();
|
|
||||||
ret += &*detailed.to_icon();
|
|
||||||
ret += " ";
|
|
||||||
ret += &*escaper::encode_minimal(&detailed.to_string_smtp(self).await);
|
|
||||||
ret += "</li></ul>";
|
|
||||||
|
|
||||||
// =============================================================================================
|
|
||||||
// Add e.g.
|
|
||||||
// Storage on testrun.org
|
|
||||||
// 1.34 GiB of 2 GiB used
|
|
||||||
// [======67%===== ]
|
|
||||||
// =============================================================================================
|
|
||||||
|
|
||||||
ret += "<h3>Message Buffers</h3>";
|
|
||||||
let transports = self
|
let transports = self
|
||||||
.sql
|
.sql
|
||||||
.query_map_vec("SELECT id, addr FROM transports", (), |row| {
|
.query_map_vec("SELECT id, addr FROM transports", (), |row| {
|
||||||
@@ -472,31 +423,71 @@ impl Context {
|
|||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
let quota = self.quota.read().await;
|
let quota = self.quota.read().await;
|
||||||
ret += "<ul>";
|
|
||||||
for (transport_id, transport_addr) in transports {
|
for (transport_id, transport_addr) in transports {
|
||||||
let domain = &deltachat_contact_tools::EmailAddress::new(&transport_addr)
|
let domain = &deltachat_contact_tools::EmailAddress::new(&transport_addr)
|
||||||
.map_or(transport_addr, |email| email.domain);
|
.map_or(transport_addr.clone(), |email| email.domain);
|
||||||
let domain_escaped = escaper::encode_minimal(domain);
|
let domain_escaped = escaper::encode_minimal(domain);
|
||||||
|
|
||||||
|
ret += "<li class=\"transport\">";
|
||||||
|
let folders = folders_states
|
||||||
|
.iter()
|
||||||
|
.filter(|(folder_addr, ..)| *folder_addr == transport_addr);
|
||||||
|
for (_addr, folder, state) in folders {
|
||||||
|
let mut folder_added = false;
|
||||||
|
|
||||||
|
if let Some(config) = folder.to_config().filter(|c| watched_folders.contains(c)) {
|
||||||
|
let f = self.get_config(config).await.log_err(self).ok().flatten();
|
||||||
|
|
||||||
|
if let Some(foldername) = f {
|
||||||
|
let detailed = &state.get_detailed();
|
||||||
|
ret += &*detailed.to_icon();
|
||||||
|
ret += " <b>";
|
||||||
|
if folder == &FolderMeaning::Inbox {
|
||||||
|
ret += &*domain_escaped;
|
||||||
|
} else {
|
||||||
|
ret += &*escaper::encode_minimal(&foldername);
|
||||||
|
}
|
||||||
|
ret += ":</b> ";
|
||||||
|
ret += &*escaper::encode_minimal(&detailed.to_string_imap(self).await);
|
||||||
|
ret += "<br />";
|
||||||
|
|
||||||
|
folder_added = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !folder_added && folder == &FolderMeaning::Inbox {
|
||||||
|
let detailed = &state.get_detailed();
|
||||||
|
if let DetailedConnectivity::Error(_) = detailed {
|
||||||
|
// On the inbox thread, we also do some other things like scan_folders and run jobs
|
||||||
|
// so, maybe, the inbox is not watched, but something else went wrong
|
||||||
|
|
||||||
|
ret += &*detailed.to_icon();
|
||||||
|
ret += " ";
|
||||||
|
ret += &*escaper::encode_minimal(&detailed.to_string_imap(self).await);
|
||||||
|
ret += "<br />";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let Some(quota) = quota.get(&transport_id) else {
|
let Some(quota) = quota.get(&transport_id) else {
|
||||||
let not_connected = stock_str::not_connected(self).await;
|
ret += "</li>";
|
||||||
ret += &format!("<li>{domain_escaped} · {not_connected}</li>");
|
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
match "a.recent {
|
match "a.recent {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let error_escaped = escaper::encode_minimal(&e.to_string());
|
ret += &escaper::encode_minimal(&e.to_string());
|
||||||
ret += &format!("<li>{domain_escaped} · {error_escaped}</li>");
|
|
||||||
}
|
}
|
||||||
Ok(quota) => {
|
Ok(quota) => {
|
||||||
if quota.is_empty() {
|
if quota.is_empty() {
|
||||||
ret += &format!(
|
ret += &format!(
|
||||||
"<li>{domain_escaped} · Warning: {domain_escaped} claims to support quota but gives no information</li>"
|
"Warning: {domain_escaped} claims to support quota but gives no information"
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
ret += "<ul class=\"quota-list\">";
|
||||||
for (root_name, resources) in quota {
|
for (root_name, resources) in quota {
|
||||||
use async_imap::types::QuotaResourceName::*;
|
use async_imap::types::QuotaResourceName::*;
|
||||||
for resource in resources {
|
for resource in resources {
|
||||||
ret += &format!("<li>{domain_escaped} · ");
|
ret += "<li>";
|
||||||
|
|
||||||
// root name is empty eg. for gmail and redundant eg. for riseup.
|
// root name is empty eg. for gmail and redundant eg. for riseup.
|
||||||
// therefore, use it only if there are really several roots.
|
// therefore, use it only if there are really several roots.
|
||||||
@@ -549,7 +540,7 @@ impl Context {
|
|||||||
} else if percent >= QUOTA_WARN_THRESHOLD_PERCENTAGE {
|
} else if percent >= QUOTA_WARN_THRESHOLD_PERCENTAGE {
|
||||||
"yellow"
|
"yellow"
|
||||||
} else {
|
} else {
|
||||||
"green"
|
"grey"
|
||||||
};
|
};
|
||||||
let div_width_percent = min(100, percent);
|
let div_width_percent = min(100, percent);
|
||||||
ret += &format!(
|
ret += &format!(
|
||||||
@@ -559,12 +550,28 @@ impl Context {
|
|||||||
ret += "</li>";
|
ret += "</li>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ret += "</ul>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ret += "</li>";
|
||||||
}
|
}
|
||||||
ret += "</ul>";
|
ret += "</ul>";
|
||||||
|
|
||||||
|
// =============================================================================================
|
||||||
|
// Add e.g.
|
||||||
|
// Outgoing messages
|
||||||
|
// Your last message was sent successfully
|
||||||
|
// =============================================================================================
|
||||||
|
|
||||||
|
let outgoing_messages = stock_str::outgoing_messages(self).await;
|
||||||
|
ret += &format!("<h3>{outgoing_messages}</h3><ul><li>");
|
||||||
|
let detailed = smtp.get_detailed();
|
||||||
|
ret += &*detailed.to_icon();
|
||||||
|
ret += " ";
|
||||||
|
ret += &*escaper::encode_minimal(&detailed.to_string_smtp(self).await);
|
||||||
|
ret += "</li></ul>";
|
||||||
|
|
||||||
// =============================================================================================
|
// =============================================================================================
|
||||||
|
|
||||||
ret += "</body></html>\n";
|
ret += "</body></html>\n";
|
||||||
|
|||||||
Reference in New Issue
Block a user