mirror of
https://github.com/chatmail/core.git
synced 2026-05-20 23:36:30 +03:00
add device message if quota is exceeding (#2621)
* resultify update_recent_quota()
* add a device-message if quota exceeds QUOTA_WARN_THRESHOLD_PERCENTAGE
* check if a quota warning should be added during housekeeping, this is at least once a day
* dc_get_config("quota_exceeding") is useful for bots
* make clippy happy
* reword QuotaExceedingMsgBody message
* avoid lots of warnings if quota jitters around the warning threshold
* allow clippy::assertions_on_constants
these constants depend on each other, it makes sense to check that they are not changed in an incompatible way
This commit is contained in:
86
src/quota.rs
86
src/quota.rs
@@ -4,13 +4,17 @@ use anyhow::{anyhow, Result};
|
||||
use async_imap::types::{Quota, QuotaResource};
|
||||
use indexmap::IndexMap;
|
||||
|
||||
use crate::chat::add_device_msg_with_importance;
|
||||
use crate::config::Config;
|
||||
use crate::constants::Viewtype;
|
||||
use crate::context::Context;
|
||||
use crate::dc_tools::time;
|
||||
use crate::imap::scan_folders::get_watched_folders;
|
||||
use crate::imap::Imap;
|
||||
use crate::job::{Action, Status};
|
||||
use crate::message::Message;
|
||||
use crate::param::Params;
|
||||
use crate::{job, EventType};
|
||||
use crate::{job, stock_str, EventType};
|
||||
|
||||
/// warn about a nearly full mailbox after this usage percentage is reached.
|
||||
/// quota icon is "yellow".
|
||||
@@ -20,6 +24,12 @@ pub const QUOTA_WARN_THRESHOLD_PERCENTAGE: u64 = 80;
|
||||
// this threshold only makes the quota icon "red".
|
||||
pub const QUOTA_ERROR_THRESHOLD_PERCENTAGE: u64 = 99;
|
||||
|
||||
/// if quota is below this value (again),
|
||||
/// QuotaExceeding is cleared.
|
||||
/// This value should be a bit below QUOTA_WARN_THRESHOLD_PERCENTAGE to
|
||||
/// avoid jittering and lots of warnings when quota is exactly at the warning threshold.
|
||||
pub const QUOTA_ALLCLEAR_PERCENTAGE: u64 = 75;
|
||||
|
||||
// if recent quota is older,
|
||||
// it is re-fetched on dc_get_connectivity_html()
|
||||
pub const QUOTA_MAX_AGE_SECONDS: i64 = 60;
|
||||
@@ -63,6 +73,29 @@ async fn get_unique_quota_roots_and_usage(
|
||||
Ok(unique_quota_roots)
|
||||
}
|
||||
|
||||
fn get_highest_usage<'t>(
|
||||
unique_quota_roots: &'t IndexMap<String, Vec<QuotaResource>>,
|
||||
) -> Result<(u64, &'t String, &QuotaResource)> {
|
||||
let mut highest: Option<(u64, &'t String, &QuotaResource)> = None;
|
||||
for (name, resources) in unique_quota_roots {
|
||||
for r in resources {
|
||||
let usage_percent = r.get_usage_percentage();
|
||||
match highest {
|
||||
None => {
|
||||
highest = Some((usage_percent, name, r));
|
||||
}
|
||||
Some((up, ..)) => {
|
||||
if up <= usage_percent {
|
||||
highest = Some((usage_percent, name, r));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
highest.ok_or_else(|| anyhow!("no quota_resource found, this is unexpected"))
|
||||
}
|
||||
|
||||
impl Context {
|
||||
// Adds a job to update `quota.recent`
|
||||
pub(crate) async fn schedule_quota_update(&self) {
|
||||
@@ -77,11 +110,17 @@ impl Context {
|
||||
/// Updates `quota.recent`, sets `quota.modified` to the current time
|
||||
/// and emits an event to let the UIs update connectivity view.
|
||||
///
|
||||
/// Moreover, once each time quota gets larger than `QUOTA_WARN_THRESHOLD_PERCENTAGE`,
|
||||
/// a device message is added.
|
||||
/// As the message is added only once, the user is not spammed
|
||||
/// in case for some providers the quota is always at ~100%
|
||||
/// and new space is allocated as needed.
|
||||
///
|
||||
/// Called in response to `Action::UpdateRecentQuota`.
|
||||
pub(crate) async fn update_recent_quota(&self, imap: &mut Imap) -> Status {
|
||||
pub(crate) async fn update_recent_quota(&self, imap: &mut Imap) -> Result<Status> {
|
||||
if let Err(err) = imap.prepare(self).await {
|
||||
warn!(self, "could not connect: {:?}", err);
|
||||
return Status::RetryNow;
|
||||
return Ok(Status::RetryNow);
|
||||
}
|
||||
|
||||
let quota = if imap.can_check_quota() {
|
||||
@@ -91,12 +130,51 @@ impl Context {
|
||||
Err(anyhow!("Quota not supported by your provider."))
|
||||
};
|
||||
|
||||
if let Ok(quota) = "a {
|
||||
match get_highest_usage(quota) {
|
||||
Ok((highest, _, _)) => {
|
||||
if highest >= QUOTA_WARN_THRESHOLD_PERCENTAGE {
|
||||
if self.get_config_int(Config::QuotaExceeding).await? == 0 {
|
||||
self.set_config(Config::QuotaExceeding, Some(&highest.to_string()))
|
||||
.await?;
|
||||
|
||||
let mut msg = Message::new(Viewtype::Text);
|
||||
msg.text = Some(stock_str::quota_exceeding(self, highest).await);
|
||||
add_device_msg_with_importance(self, None, Some(&mut msg), true)
|
||||
.await?;
|
||||
}
|
||||
} else if highest <= QUOTA_ALLCLEAR_PERCENTAGE {
|
||||
self.set_config(Config::QuotaExceeding, None).await?;
|
||||
}
|
||||
}
|
||||
Err(err) => warn!(self, "cannot get highest quota usage: {:?}", err),
|
||||
}
|
||||
}
|
||||
|
||||
*self.quota.write().await = Some(QuotaInfo {
|
||||
recent: quota,
|
||||
modified: time(),
|
||||
});
|
||||
|
||||
self.emit_event(EventType::ConnectivityChanged);
|
||||
Status::Finished(Ok(()))
|
||||
Ok(Status::Finished(Ok(())))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::quota::{
|
||||
QUOTA_ALLCLEAR_PERCENTAGE, QUOTA_ERROR_THRESHOLD_PERCENTAGE,
|
||||
QUOTA_WARN_THRESHOLD_PERCENTAGE,
|
||||
};
|
||||
|
||||
#[allow(clippy::assertions_on_constants)]
|
||||
#[async_std::test]
|
||||
async fn test_quota_thresholds() -> anyhow::Result<()> {
|
||||
assert!(QUOTA_ALLCLEAR_PERCENTAGE > 50);
|
||||
assert!(QUOTA_ALLCLEAR_PERCENTAGE < QUOTA_WARN_THRESHOLD_PERCENTAGE);
|
||||
assert!(QUOTA_WARN_THRESHOLD_PERCENTAGE < QUOTA_ERROR_THRESHOLD_PERCENTAGE);
|
||||
assert!(QUOTA_ERROR_THRESHOLD_PERCENTAGE < 100);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user