Add automatic per-email address unread folders
This commit is contained in:
parent
2e5b18a008
commit
aa2a9815df
83
Cargo.lock
generated
83
Cargo.lock
generated
@ -464,9 +464,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitcode"
|
name = "bitcode"
|
||||||
version = "0.6.4"
|
version = "0.6.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2ae9f4d868fa036ee7517b997dc2b6efd383a8b2efdcb4b07058260de060d3ef"
|
checksum = "18c1406a27371b2f76232a2259df6ab607b91b5a0a7476a7729ff590df5a969a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec 0.7.6",
|
"arrayvec 0.7.6",
|
||||||
"bitcode_derive",
|
"bitcode_derive",
|
||||||
@ -477,9 +477,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitcode_derive"
|
name = "bitcode_derive"
|
||||||
version = "0.6.4"
|
version = "0.6.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7d0b1506d8de85c2198149fb9f6285e6b662a867f84579af2b3ef067a8842d35"
|
checksum = "42b6b4cb608b8282dc3b53d0f4c9ab404655d562674c682db7e6c0458cc83c23"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -715,9 +715,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.14"
|
version = "1.2.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0c3d1b2e905a3a7b00a6141adb0e4c0bb941d11caf55349d863942a1cc44e3c9"
|
checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jobserver",
|
"jobserver",
|
||||||
"libc",
|
"libc",
|
||||||
@ -1888,9 +1888,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glam"
|
name = "glam"
|
||||||
version = "0.29.2"
|
version = "0.30.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dc46dd3ec48fdd8e693a98d2b8bafae273a2d54c1de02a2a7e3d57d501f39677"
|
checksum = "17fcdf9683c406c2fc4d124afd29c0d595e22210d633cbdb8695ba9935ab1dc6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glob"
|
name = "glob"
|
||||||
@ -2089,9 +2089,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.4.7"
|
version = "0.4.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e"
|
checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atomic-waker",
|
"atomic-waker",
|
||||||
"bytes 1.10.0",
|
"bytes 1.10.0",
|
||||||
@ -2448,7 +2448,7 @@ dependencies = [
|
|||||||
"bytes 1.10.0",
|
"bytes 1.10.0",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"h2 0.4.7",
|
"h2 0.4.8",
|
||||||
"http 1.2.0",
|
"http 1.2.0",
|
||||||
"http-body 1.0.1",
|
"http-body 1.0.1",
|
||||||
"httparse",
|
"httparse",
|
||||||
@ -2916,6 +2916,7 @@ version = "0.6.1"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"itertools 0.14.0",
|
"itertools 0.14.0",
|
||||||
"log",
|
"log",
|
||||||
|
"mailparse",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"rayon",
|
"rayon",
|
||||||
"serde",
|
"serde",
|
||||||
@ -3363,9 +3364,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.8.4"
|
version = "0.8.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b3b1c9bd4fe1f0f8b387f6eb9eb3b4a1aa26185e5750efb9140301703f62cd1b"
|
checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"adler2",
|
"adler2",
|
||||||
]
|
]
|
||||||
@ -3471,9 +3472,9 @@ checksum = "2195bf6aa996a481483b29d62a7663eed3fe39600c460e323f8ff41e90bdd89b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "native-tls"
|
name = "native-tls"
|
||||||
version = "0.2.13"
|
version = "0.2.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0dab59f8e050d5df8e4dd87d9206fb6f65a483e20ac9fda365ade4fab353196c"
|
checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
@ -3640,9 +3641,9 @@ checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oneshot"
|
name = "oneshot"
|
||||||
version = "0.1.10"
|
version = "0.1.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "79d72a7c0f743d2ebb0a2ad1d219db75fdc799092ed3a884c9144c42a31225bd"
|
checksum = "b4ce411919553d3f9fa53a0880544cda985a112117a0444d5ff1e870a893d6ea"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opaque-debug"
|
name = "opaque-debug"
|
||||||
@ -3907,7 +3908,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall 0.5.8",
|
"redox_syscall 0.5.9",
|
||||||
"smallvec 1.14.0",
|
"smallvec 1.14.0",
|
||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
@ -4581,9 +4582,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.5.8"
|
version = "0.5.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
|
checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.8.0",
|
"bitflags 2.8.0",
|
||||||
]
|
]
|
||||||
@ -4675,7 +4676,7 @@ dependencies = [
|
|||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"h2 0.4.7",
|
"h2 0.4.8",
|
||||||
"http 1.2.0",
|
"http 1.2.0",
|
||||||
"http-body 1.0.1",
|
"http-body 1.0.1",
|
||||||
"http-body-util",
|
"http-body-util",
|
||||||
@ -4715,9 +4716,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ring"
|
name = "ring"
|
||||||
version = "0.17.9"
|
version = "0.17.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e75ec5e92c4d8aede845126adc388046234541629e76029599ed35a003c7ed24"
|
checksum = "da5349ae27d3887ca812fb375b45a4fbb36d8d12d2df394968cd86e35683fe73"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
@ -5777,9 +5778,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "string_cache_codegen"
|
name = "string_cache_codegen"
|
||||||
version = "0.5.3"
|
version = "0.5.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "244292f3441c89febe5b5bdfbb6863aeaf4f64da810ea3050fd927b27b8d92ce"
|
checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"phf_generator 0.11.3",
|
"phf_generator 0.11.3",
|
||||||
"phf_shared 0.11.3",
|
"phf_shared 0.11.3",
|
||||||
@ -6050,9 +6051,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "3.17.0"
|
version = "3.17.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a40f762a77d2afa88c2d919489e390a12bdd261ed568e60cfa7e48d4e20f0d33"
|
checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"fastrand",
|
"fastrand",
|
||||||
@ -6538,7 +6539,7 @@ dependencies = [
|
|||||||
"axum",
|
"axum",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"bytes 1.10.0",
|
"bytes 1.10.0",
|
||||||
"h2 0.4.7",
|
"h2 0.4.8",
|
||||||
"http 1.2.0",
|
"http 1.2.0",
|
||||||
"http-body 1.0.1",
|
"http-body 1.0.1",
|
||||||
"http-body-util",
|
"http-body-util",
|
||||||
@ -6730,9 +6731,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.17.0"
|
version = "1.18.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ubyte"
|
name = "ubyte"
|
||||||
@ -6783,9 +6784,9 @@ checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.16"
|
version = "1.0.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
|
checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-normalization"
|
name = "unicode-normalization"
|
||||||
@ -7096,7 +7097,7 @@ version = "1.5.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d"
|
checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"redox_syscall 0.5.8",
|
"redox_syscall 0.5.9",
|
||||||
"wasite",
|
"wasite",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
]
|
]
|
||||||
@ -7342,9 +7343,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.7.2"
|
version = "0.7.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603"
|
checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
@ -7517,27 +7518,27 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zstd"
|
name = "zstd"
|
||||||
version = "0.13.2"
|
version = "0.13.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9"
|
checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zstd-safe",
|
"zstd-safe",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zstd-safe"
|
name = "zstd-safe"
|
||||||
version = "7.2.1"
|
version = "7.2.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059"
|
checksum = "f3051792fbdc2e1e143244dc28c60f73d8470e93f3f9cbd0ead44da5ed802722"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zstd-sys",
|
"zstd-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zstd-sys"
|
name = "zstd-sys"
|
||||||
version = "2.0.13+zstd.1.5.6"
|
version = "2.0.14+zstd.1.5.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa"
|
checksum = "8fb060d4926e4ac3a3ad15d864e99ceb5f343c6b34f5bd6d81ae6ed417311be5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
|
|||||||
@ -11,6 +11,7 @@ publish = ["xinu"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
|
mailparse = "0.16.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = { version = "1.0", features = ["unbounded_depth"] }
|
serde_json = { version = "1.0", features = ["unbounded_depth"] }
|
||||||
thiserror = "2.0.0"
|
thiserror = "2.0.0"
|
||||||
|
|||||||
@ -207,6 +207,7 @@
|
|||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
ffi::OsStr,
|
ffi::OsStr,
|
||||||
io::{self},
|
io::{self},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
@ -459,6 +460,8 @@ pub enum NotmuchError {
|
|||||||
StringUtf8Error(#[from] std::string::FromUtf8Error),
|
StringUtf8Error(#[from] std::string::FromUtf8Error),
|
||||||
#[error("failed to parse str as int")]
|
#[error("failed to parse str as int")]
|
||||||
ParseIntError(#[from] std::num::ParseIntError),
|
ParseIntError(#[from] std::num::ParseIntError),
|
||||||
|
#[error("failed to parse mail: {0}")]
|
||||||
|
MailParseError(#[from] mailparse::MailParseError),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -605,6 +608,59 @@ impl Notmuch {
|
|||||||
Ok(serde_json::from_slice(&res)?)
|
Ok(serde_json::from_slice(&res)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(skip_all)]
|
||||||
|
pub fn unread_recipients(&self) -> Result<HashMap<String, usize>, NotmuchError> {
|
||||||
|
let slice = self.run_notmuch([
|
||||||
|
"show",
|
||||||
|
"--include-html=false",
|
||||||
|
"--entire-thread=false",
|
||||||
|
"--body=false",
|
||||||
|
"--format=json",
|
||||||
|
// Arbitrary limit to prevent too much work
|
||||||
|
"--limit=1000",
|
||||||
|
"is:unread",
|
||||||
|
])?;
|
||||||
|
// Notmuch returns JSON with invalid unicode. So we lossy convert it to a string here and
|
||||||
|
// use that for parsing in rust.
|
||||||
|
let s = String::from_utf8_lossy(&slice);
|
||||||
|
let mut deserializer = serde_json::Deserializer::from_str(&s);
|
||||||
|
deserializer.disable_recursion_limit();
|
||||||
|
let ts: ThreadSet = serde::de::Deserialize::deserialize(&mut deserializer)?;
|
||||||
|
deserializer.end()?;
|
||||||
|
let mut r = HashMap::new();
|
||||||
|
for t in ts.0 {
|
||||||
|
for tn in t.0 {
|
||||||
|
let Some(msg) = tn.0 else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let mut addrs = vec![];
|
||||||
|
let hdr = msg.headers.to;
|
||||||
|
if let Some(to) = hdr {
|
||||||
|
addrs.push(to);
|
||||||
|
};
|
||||||
|
let hdr = msg.headers.cc;
|
||||||
|
if let Some(cc) = hdr {
|
||||||
|
addrs.push(cc);
|
||||||
|
};
|
||||||
|
for recipient in addrs {
|
||||||
|
mailparse::addrparse(&recipient)?
|
||||||
|
.into_inner()
|
||||||
|
.iter()
|
||||||
|
.for_each(|a| {
|
||||||
|
let mailparse::MailAddr::Single(si) = a else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let addr = &si.addr;
|
||||||
|
if addr == "couchmoney@gmail.com" || addr.ends_with("@xinu.tv") {
|
||||||
|
*r.entry(addr.clone()).or_default() += 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(r)
|
||||||
|
}
|
||||||
|
|
||||||
fn run_notmuch<I, S>(&self, args: I) -> Result<Vec<u8>, NotmuchError>
|
fn run_notmuch<I, S>(&self, args: I) -> Result<Vec<u8>, NotmuchError>
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = S>,
|
I: IntoIterator<Item = S>,
|
||||||
|
|||||||
@ -793,7 +793,17 @@ impl FromStr for Query {
|
|||||||
if word == "is:unread" {
|
if word == "is:unread" {
|
||||||
unread_only = true
|
unread_only = true
|
||||||
} else if word.starts_with("tag:") {
|
} else if word.starts_with("tag:") {
|
||||||
tags.push(word["tag:".len()..].to_string());
|
let t = &word["tag:".len()..];
|
||||||
|
// Per-address emails are faked as `tag:@<domain>/<username>`, rewrite to `to:` form
|
||||||
|
if t.starts_with('@') && t.contains('.') {
|
||||||
|
let t = match t.split_once('/') {
|
||||||
|
None => format!("to:{t}"),
|
||||||
|
Some((domain, user)) => format!("to:{user}{domain}"),
|
||||||
|
};
|
||||||
|
remainder.push(t);
|
||||||
|
} else {
|
||||||
|
tags.push(t.to_string());
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
} else if word.starts_with("tag:") {
|
} else if word.starts_with("tag:") {
|
||||||
|
|||||||
@ -1,11 +1,7 @@
|
|||||||
use std::{
|
use std::{collections::HashMap, fs::File};
|
||||||
collections::HashMap,
|
|
||||||
fs::File,
|
|
||||||
hash::{DefaultHasher, Hash, Hasher},
|
|
||||||
time::Instant,
|
|
||||||
};
|
|
||||||
|
|
||||||
use letterbox_notmuch::Notmuch;
|
use letterbox_notmuch::Notmuch;
|
||||||
|
use letterbox_shared::compute_color;
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
use mailparse::{parse_content_type, parse_mail, MailHeader, MailHeaderMap, ParsedMail};
|
use mailparse::{parse_content_type, parse_mail, MailHeader, MailHeaderMap, ParsedMail};
|
||||||
use memmap::MmapOptions;
|
use memmap::MmapOptions;
|
||||||
@ -107,7 +103,6 @@ pub async fn search(
|
|||||||
|
|
||||||
#[instrument(name="nm::tags", skip_all, fields(needs_unread=needs_unread))]
|
#[instrument(name="nm::tags", skip_all, fields(needs_unread=needs_unread))]
|
||||||
pub fn tags(nm: &Notmuch, needs_unread: bool) -> Result<Vec<Tag>, ServerError> {
|
pub fn tags(nm: &Notmuch, needs_unread: bool) -> Result<Vec<Tag>, ServerError> {
|
||||||
let now = Instant::now();
|
|
||||||
let unread_msg_cnt: HashMap<String, usize> = if needs_unread {
|
let unread_msg_cnt: HashMap<String, usize> = if needs_unread {
|
||||||
// 10000 is an arbitrary number, if there's more than 10k unread messages, we'll
|
// 10000 is an arbitrary number, if there's more than 10k unread messages, we'll
|
||||||
// get an inaccurate count.
|
// get an inaccurate count.
|
||||||
@ -123,13 +118,11 @@ pub fn tags(nm: &Notmuch, needs_unread: bool) -> Result<Vec<Tag>, ServerError> {
|
|||||||
} else {
|
} else {
|
||||||
HashMap::new()
|
HashMap::new()
|
||||||
};
|
};
|
||||||
let tags = nm
|
let tags: Vec<_> = nm
|
||||||
.tags()?
|
.tags()?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|tag| {
|
.map(|tag| {
|
||||||
let mut hasher = DefaultHasher::new();
|
let hex = compute_color(&tag);
|
||||||
tag.hash(&mut hasher);
|
|
||||||
let hex = format!("#{:06x}", hasher.finish() % (1 << 24));
|
|
||||||
let unread = if needs_unread {
|
let unread = if needs_unread {
|
||||||
*unread_msg_cnt.get(&tag).unwrap_or(&0)
|
*unread_msg_cnt.get(&tag).unwrap_or(&0)
|
||||||
} else {
|
} else {
|
||||||
@ -142,8 +135,24 @@ pub fn tags(nm: &Notmuch, needs_unread: bool) -> Result<Vec<Tag>, ServerError> {
|
|||||||
unread,
|
unread,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.chain(
|
||||||
|
nm.unread_recipients()?
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|(name, unread)| {
|
||||||
|
let Some(idx) = name.find('@') else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
let name = format!("{}/{}", &name[idx..], &name[..idx]);
|
||||||
|
let bg_color = compute_color(&name);
|
||||||
|
Some(Tag {
|
||||||
|
name,
|
||||||
|
fg_color: "white".to_string(),
|
||||||
|
bg_color,
|
||||||
|
unread,
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
)
|
||||||
.collect();
|
.collect();
|
||||||
info!("Fetching tags took {} seconds", now.elapsed().as_secs_f32());
|
|
||||||
Ok(tags)
|
Ok(tags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use std::collections::HashSet;
|
use std::{cmp::Ordering, collections::HashSet};
|
||||||
|
|
||||||
use chrono::{DateTime, Datelike, Duration, Local, Utc};
|
use chrono::{DateTime, Datelike, Duration, Local, Utc};
|
||||||
use human_format::{Formatter, Scales};
|
use human_format::{Formatter, Scales};
|
||||||
@ -1006,11 +1006,26 @@ pub fn tags(model: &Model) -> Node<Msg> {
|
|||||||
}
|
}
|
||||||
tag_els
|
tag_els
|
||||||
}
|
}
|
||||||
let unread = model
|
let mut unread = model
|
||||||
.tags
|
.tags
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|tags| tags.iter().filter(|t| t.unread > 0).collect())
|
.map(|tags| tags.iter().filter(|t| t.unread > 0).collect())
|
||||||
.unwrap_or(Vec::new());
|
.unwrap_or(Vec::new());
|
||||||
|
unread.sort_by(|a, b| {
|
||||||
|
let r = if a.name.starts_with('@') && b.name.starts_with('@') {
|
||||||
|
a.name.cmp(&b.name)
|
||||||
|
} else if a.name.starts_with('@') {
|
||||||
|
Ordering::Less
|
||||||
|
} else if b.name.starts_with('@') {
|
||||||
|
Ordering::Greater
|
||||||
|
} else {
|
||||||
|
a.name.cmp(&b.name)
|
||||||
|
};
|
||||||
|
if a.name.starts_with('@') || b.name.starts_with('@') {
|
||||||
|
info!("a {} < b {} = {r:?}", a.name, b.name,);
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
});
|
||||||
let tags_open = use_state(|| false);
|
let tags_open = use_state(|| false);
|
||||||
let force_tags_open = unread.is_empty();
|
let force_tags_open = unread.is_empty();
|
||||||
aside![
|
aside![
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user