Compare commits

..

14 Commits

Author SHA1 Message Date
c0982e82c6 chore: Release
All checks were successful
Continuous integration / Check (push) Successful in 58s
Continuous integration / Test Suite (push) Successful in 1m26s
Continuous integration / Trunk (push) Successful in 1m1s
Continuous integration / Rustfmt (push) Successful in 45s
Continuous integration / build (push) Successful in 1m48s
Continuous integration / Disallow unused dependencies (push) Successful in 2m33s
2025-11-03 17:19:22 -08:00
8971fe3b6b web: fix lint 2025-11-03 17:19:08 -08:00
243e35ec15 chore: Release 2025-11-03 15:43:26 -08:00
4cf1f882b8 cargo sqlx prepare 2025-11-03 15:43:25 -08:00
a8129e4685 Upsert snoozes and mark snoozed messages as read 2025-11-03 15:42:44 -08:00
50a4bfcac7 More implementation 2025-11-03 15:42:44 -08:00
90ac9a1e43 snooze: add UI elements and DB for snooze functionality 2025-11-03 15:42:44 -08:00
52b19365d7 Regen Cargo.lock 2025-11-03 15:42:44 -08:00
399865f5f7 snooze: add UI elements and DB for snooze functionality 2025-11-03 15:42:44 -08:00
2eb4784e83 WIP snooze feature 2025-11-03 15:42:44 -08:00
be2085b397 Merge pull request 'chore(deps): lock file maintenance' (#185) from renovate/lock-file-maintenance into master
All checks were successful
Continuous integration / Check (push) Successful in 1m4s
Continuous integration / Test Suite (push) Successful in 1m29s
Continuous integration / Trunk (push) Successful in 7m47s
Continuous integration / Rustfmt (push) Successful in 40s
Continuous integration / build (push) Successful in 1m50s
Continuous integration / Disallow unused dependencies (push) Successful in 2m12s
2025-11-02 17:02:02 -08:00
2837ea835a chore(deps): lock file maintenance
All checks were successful
Continuous integration / Check (push) Successful in 2m12s
Continuous integration / Test Suite (push) Successful in 4m19s
Continuous integration / Trunk (push) Successful in 7m50s
Continuous integration / Rustfmt (push) Successful in 43s
Continuous integration / build (push) Successful in 4m24s
Continuous integration / Disallow unused dependencies (push) Successful in 2m38s
2025-11-03 00:02:52 +00:00
a84e673d88 Merge pull request 'fix(deps): update all non-major dependencies' (#184) from renovate/all-minor-patch into master
All checks were successful
Continuous integration / Check (push) Successful in 55s
Continuous integration / Test Suite (push) Successful in 1m19s
Continuous integration / Trunk (push) Successful in 56s
Continuous integration / Rustfmt (push) Successful in 43s
Continuous integration / build (push) Successful in 1m32s
Continuous integration / Disallow unused dependencies (push) Successful in 2m11s
2025-11-01 05:46:41 -07:00
2bc840a4e2 fix(deps): update all non-major dependencies
All checks were successful
Continuous integration / Check (push) Successful in 2m22s
Continuous integration / Test Suite (push) Successful in 4m22s
Continuous integration / Trunk (push) Successful in 7m37s
Continuous integration / Rustfmt (push) Successful in 41s
Continuous integration / build (push) Successful in 5m3s
Continuous integration / Disallow unused dependencies (push) Successful in 2m16s
2025-11-01 12:16:43 +00:00
20 changed files with 379 additions and 163 deletions

219
Cargo.lock generated
View File

@@ -42,9 +42,9 @@ dependencies = [
[[package]]
name = "aho-corasick"
version = "1.1.3"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
dependencies = [
"memchr",
]
@@ -233,6 +233,7 @@ dependencies = [
"async-trait",
"base64 0.22.1",
"bytes 1.10.1",
"chrono",
"fast_chemail",
"fnv",
"futures-timer",
@@ -906,9 +907,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.2.43"
version = "1.2.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "739eb0f94557554b3ca9a86d2d37bebd49c5e6d0c1d2bda35ba5bdac830befc2"
checksum = "37521ac7aabe3d13122dc382493e20c9416f299d2ccd5b3a5340a2570cdeb0f3"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -1343,9 +1344,9 @@ dependencies = [
[[package]]
name = "css-inline"
version = "0.17.0"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30983955a71311b6268dca9ea211b75136367ab3ed7d27d78617587d7d9beebf"
checksum = "e718bae3215a5fe4d70ce7adad0ae626a34e9549d252ff5cb59ec71aff1f96cf"
dependencies = [
"cssparser",
"html5ever",
@@ -1355,7 +1356,7 @@ dependencies = [
"rayon",
"reqwest",
"rustc-hash",
"selectors 0.30.0",
"selectors 0.32.0",
"smallvec 1.15.1",
"url",
]
@@ -2092,9 +2093,9 @@ dependencies = [
[[package]]
name = "glam"
version = "0.30.8"
version = "0.30.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e12d847aeb25f41be4c0ec9587d624e9cd631bc007a8fd7ce3f5851e064c6460"
checksum = "bd47b05dddf0005d850e5644cae7f2b14ac3df487979dbfff3b56f20b1a6ae46"
[[package]]
name = "glob"
@@ -2857,9 +2858,9 @@ dependencies = [
[[package]]
name = "icu_collections"
version = "2.0.0"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47"
checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43"
dependencies = [
"displaydoc",
"potential_utf",
@@ -2870,9 +2871,9 @@ dependencies = [
[[package]]
name = "icu_locale_core"
version = "2.0.0"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a"
checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6"
dependencies = [
"displaydoc",
"litemap",
@@ -2883,11 +2884,10 @@ dependencies = [
[[package]]
name = "icu_normalizer"
version = "2.0.0"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979"
checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599"
dependencies = [
"displaydoc",
"icu_collections",
"icu_normalizer_data",
"icu_properties",
@@ -2898,42 +2898,38 @@ dependencies = [
[[package]]
name = "icu_normalizer_data"
version = "2.0.0"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3"
checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a"
[[package]]
name = "icu_properties"
version = "2.0.1"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b"
checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99"
dependencies = [
"displaydoc",
"icu_collections",
"icu_locale_core",
"icu_properties_data",
"icu_provider",
"potential_utf",
"zerotrie",
"zerovec",
]
[[package]]
name = "icu_properties_data"
version = "2.0.1"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632"
checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899"
[[package]]
name = "icu_provider"
version = "2.0.0"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af"
checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614"
dependencies = [
"displaydoc",
"icu_locale_core",
"stable_deref_trait",
"tinystr",
"writeable",
"yoke",
"zerofrom",
@@ -3147,6 +3143,20 @@ dependencies = [
[[package]]
name = "letterbox-notmuch"
version = "0.17.45"
source = "sparse+https://git.z.xinu.tv/api/packages/wathiede/cargo/"
checksum = "030ae3b352f749b387ff4c09d8dd0d01859db86c83a848ca3b6fabd6c61e6edb"
dependencies = [
"log",
"mailparse",
"serde",
"serde_json",
"thiserror 2.0.17",
"tracing",
]
[[package]]
name = "letterbox-notmuch"
version = "0.17.47"
dependencies = [
"itertools",
"log",
@@ -3159,28 +3169,14 @@ dependencies = [
"tracing",
]
[[package]]
name = "letterbox-notmuch"
version = "0.17.45"
source = "sparse+https://git.z.xinu.tv/api/packages/wathiede/cargo/"
checksum = "030ae3b352f749b387ff4c09d8dd0d01859db86c83a848ca3b6fabd6c61e6edb"
dependencies = [
"log",
"mailparse",
"serde",
"serde_json",
"thiserror 2.0.17",
"tracing",
]
[[package]]
name = "letterbox-procmail2notmuch"
version = "0.17.45"
version = "0.17.47"
dependencies = [
"anyhow",
"clap",
"letterbox-notmuch 0.17.45 (sparse+https://git.z.xinu.tv/api/packages/wathiede/cargo/)",
"letterbox-shared 0.17.45 (sparse+https://git.z.xinu.tv/api/packages/wathiede/cargo/)",
"letterbox-notmuch 0.17.45",
"letterbox-shared 0.17.45",
"serde",
"sqlx",
"tokio 1.48.0",
@@ -3188,7 +3184,7 @@ dependencies = [
[[package]]
name = "letterbox-server"
version = "0.17.45"
version = "0.17.47"
dependencies = [
"ammonia",
"anyhow",
@@ -3211,8 +3207,8 @@ dependencies = [
"html-escape",
"html2text",
"ical",
"letterbox-notmuch 0.17.45",
"letterbox-shared 0.17.45",
"letterbox-notmuch 0.17.47",
"letterbox-shared 0.17.47",
"linkify",
"lol_html",
"mailparse",
@@ -3239,8 +3235,10 @@ dependencies = [
[[package]]
name = "letterbox-shared"
version = "0.17.45"
source = "sparse+https://git.z.xinu.tv/api/packages/wathiede/cargo/"
checksum = "2a541a4fce95b2f59de57c578cb7c2958ff48474cea4c619aacd1738ce3e7e0d"
dependencies = [
"build-info 0.0.42",
"build-info 0.0.41",
"letterbox-notmuch 0.17.45",
"regex",
"serde",
@@ -3251,12 +3249,10 @@ dependencies = [
[[package]]
name = "letterbox-shared"
version = "0.17.45"
source = "sparse+https://git.z.xinu.tv/api/packages/wathiede/cargo/"
checksum = "2a541a4fce95b2f59de57c578cb7c2958ff48474cea4c619aacd1738ce3e7e0d"
version = "0.17.47"
dependencies = [
"build-info 0.0.41",
"letterbox-notmuch 0.17.45 (sparse+https://git.z.xinu.tv/api/packages/wathiede/cargo/)",
"build-info 0.0.42",
"letterbox-notmuch 0.17.47",
"regex",
"serde",
"sqlx",
@@ -3266,7 +3262,7 @@ dependencies = [
[[package]]
name = "letterbox-web"
version = "0.17.45"
version = "0.17.47"
dependencies = [
"build-info 0.0.42",
"build-info-build",
@@ -3278,7 +3274,7 @@ dependencies = [
"graphql_client",
"human_format",
"itertools",
"letterbox-shared 0.17.45",
"letterbox-shared 0.17.47",
"log",
"seed",
"seed_hooks",
@@ -3387,9 +3383,9 @@ checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
[[package]]
name = "litemap"
version = "0.8.0"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77"
[[package]]
name = "lock_api"
@@ -3821,11 +3817,10 @@ dependencies = [
[[package]]
name = "num-bigint-dig"
version = "0.8.4"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151"
checksum = "82c79c15c05d4bf82b6f5ef163104cc81a760d8e874d38ac50ab67c8877b647b"
dependencies = [
"byteorder",
"lazy_static",
"libm",
"num-integer",
@@ -4339,9 +4334,9 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "potential_utf"
version = "0.1.3"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a"
checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77"
dependencies = [
"zerovec",
]
@@ -4354,9 +4349,9 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "ppmd-rust"
version = "1.2.1"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c834641d8ad1b348c9ee86dec3b9840d805acd5f24daa5f90c788951a52ff59b"
checksum = "d558c559f0450f16f2a27a1f017ef38468c1090c9ce63c8e51366232d53717b4"
[[package]]
name = "ppv-lite86"
@@ -4551,7 +4546,7 @@ dependencies = [
"once_cell",
"socket2 0.6.1",
"tracing",
"windows-sys 0.52.0",
"windows-sys 0.60.2",
]
[[package]]
@@ -5109,9 +5104,9 @@ dependencies = [
[[package]]
name = "rustls-pki-types"
version = "1.12.0"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79"
checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a"
dependencies = [
"web-time",
"zeroize",
@@ -5119,9 +5114,9 @@ dependencies = [
[[package]]
name = "rustls-webpki"
version = "0.103.7"
version = "0.103.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e10b3f4191e8a80e6b43eebabfac91e5dcecebb27a71f04e820c47ec41d314bf"
checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52"
dependencies = [
"ring",
"rustls-pki-types",
@@ -5252,25 +5247,6 @@ dependencies = [
"web-sys",
]
[[package]]
name = "selectors"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3df44ba8a7ca7a4d28c589e04f526266ed76b6cc556e33fe69fa25de31939a65"
dependencies = [
"bitflags 2.10.0",
"cssparser",
"derive_more",
"fxhash",
"log",
"new_debug_unreachable",
"phf 0.11.3",
"phf_codegen",
"precomputed-hash",
"servo_arc",
"smallvec 1.15.1",
]
[[package]]
name = "selectors"
version = "0.31.0"
@@ -5627,6 +5603,7 @@ checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6"
dependencies = [
"base64 0.22.1",
"bytes 1.10.1",
"chrono",
"crc",
"crossbeam-queue 0.3.12",
"either",
@@ -5647,7 +5624,6 @@ dependencies = [
"sha2 0.10.9",
"smallvec 1.15.1",
"thiserror 2.0.17",
"time 0.3.44",
"tokio 1.48.0",
"tokio-stream",
"tracing",
@@ -5703,6 +5679,7 @@ dependencies = [
"bitflags 2.10.0",
"byteorder",
"bytes 1.10.1",
"chrono",
"crc",
"digest 0.10.7",
"dotenvy",
@@ -5730,7 +5707,6 @@ dependencies = [
"sqlx-core",
"stringprep",
"thiserror 2.0.17",
"time 0.3.44",
"tracing",
"whoami",
]
@@ -5745,6 +5721,7 @@ dependencies = [
"base64 0.22.1",
"bitflags 2.10.0",
"byteorder",
"chrono",
"crc",
"dotenvy",
"etcetera",
@@ -5768,7 +5745,6 @@ dependencies = [
"sqlx-core",
"stringprep",
"thiserror 2.0.17",
"time 0.3.44",
"tracing",
"whoami",
]
@@ -5780,6 +5756,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea"
dependencies = [
"atoi",
"chrono",
"flume",
"futures-channel",
"futures-core",
@@ -5793,7 +5770,6 @@ dependencies = [
"serde_urlencoded",
"sqlx-core",
"thiserror 2.0.17",
"time 0.3.44",
"tracing",
"url",
]
@@ -6239,9 +6215,9 @@ dependencies = [
[[package]]
name = "tinystr"
version = "0.8.1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b"
checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869"
dependencies = [
"displaydoc",
"zerovec",
@@ -6564,9 +6540,9 @@ dependencies = [
[[package]]
name = "tokio-util"
version = "0.7.16"
version = "0.7.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5"
checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594"
dependencies = [
"bytes 1.10.1",
"futures-core",
@@ -6870,24 +6846,24 @@ checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
[[package]]
name = "unicode-ident"
version = "1.0.20"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "462eeb75aeb73aea900253ce739c8e18a67423fadf006037cd3ff27e82748a06"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "unicode-normalization"
version = "0.1.24"
version = "0.1.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956"
checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8"
dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-properties"
version = "0.1.3"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0"
checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d"
[[package]]
name = "unicode-width"
@@ -7168,9 +7144,9 @@ dependencies = [
[[package]]
name = "webpki-roots"
version = "1.0.3"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32b130c0d2d49f8b6889abc456e795e82525204f27c42cf767cf0d7734e089b8"
checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e"
dependencies = [
"rustls-pki-types",
]
@@ -7562,9 +7538,9 @@ checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
[[package]]
name = "writeable"
version = "0.6.1"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
[[package]]
name = "ws2_32-sys"
@@ -7578,9 +7554,9 @@ dependencies = [
[[package]]
name = "xml-rs"
version = "0.8.27"
version = "0.8.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7"
checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f"
[[package]]
name = "xtracing"
@@ -7614,11 +7590,10 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
[[package]]
name = "yoke"
version = "0.8.0"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc"
checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954"
dependencies = [
"serde",
"stable_deref_trait",
"yoke-derive",
"zerofrom",
@@ -7626,9 +7601,9 @@ dependencies = [
[[package]]
name = "yoke-derive"
version = "0.8.0"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d"
dependencies = [
"proc-macro2",
"quote",
@@ -7699,9 +7674,9 @@ dependencies = [
[[package]]
name = "zerotrie"
version = "0.2.2"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595"
checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851"
dependencies = [
"displaydoc",
"yoke",
@@ -7710,9 +7685,9 @@ dependencies = [
[[package]]
name = "zerovec"
version = "0.11.4"
version = "0.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b"
checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002"
dependencies = [
"yoke",
"zerofrom",
@@ -7721,9 +7696,9 @@ dependencies = [
[[package]]
name = "zerovec-derive"
version = "0.11.1"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3"
dependencies = [
"proc-macro2",
"quote",
@@ -7765,9 +7740,9 @@ checksum = "2f06ae92f42f5e5c42443fd094f245eb656abf56dd7cce9b8b263236565e00f2"
[[package]]
name = "zopfli"
version = "0.8.2"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edfc5ee405f504cd4984ecc6f14d02d55cfda60fa4b689434ef4102aae150cd7"
checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249"
dependencies = [
"bumpalo",
"crc32fast",

View File

@@ -8,7 +8,7 @@ authors = ["Bill Thiede <git@xinu.tv>"]
edition = "2021"
license = "UNLICENSED"
publish = ["xinu"]
version = "0.17.45"
version = "0.17.47"
repository = "https://git.z.xinu.tv/wathiede/letterbox"
[profile.dev]

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "SELECT\n p.id,\n link,\n clean_summary\nFROM\n post AS p\nINNER JOIN feed AS f ON p.site = f.slug -- necessary to weed out nzb posts\nWHERE\n search_summary IS NULL\n -- TODO remove AND link ~ '^<'\nORDER BY\n ROW_NUMBER() OVER (PARTITION BY site ORDER BY date DESC)\nLIMIT 100;\n",
"query": "SELECT\n p.id,\n link,\n clean_summary\nFROM\n post AS p\nINNER JOIN feed AS f ON p.site = f.slug -- necessary to weed out nzb posts\nWHERE\n search_summary IS NULL\n -- TODO remove AND link ~ '^<'\nORDER BY\n ROW_NUMBER() OVER (PARTITION BY site ORDER BY date DESC)\nLIMIT 1000;\n",
"describe": {
"columns": [
{
@@ -28,5 +28,5 @@
true
]
},
"hash": "3d271b404f06497a5dcde68cf6bf07291d70fa56058ea736ac24e91d33050c04"
"hash": "cf369e3d5547f400cb54004dd03783ef6998a000aec91c50a79405dcf1c53b17"
}

View File

@@ -0,0 +1,15 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO snooze (message_id, wake)\n VALUES ($1, $2)\n ON CONFLICT (message_id) DO UPDATE\n SET wake = $2\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text",
"Timestamptz"
]
},
"nullable": []
},
"hash": "effd0d0d91e6ad84546f7177f1fd39d4fad736b471eb5e55fd5ac74f7adff664"
}

View File

@@ -17,7 +17,7 @@ html2text = "0.16"
ammonia = "4.1.0"
anyhow = "1.0.98"
askama = { version = "0.14.0", features = ["derive"] }
async-graphql = { version = "7", features = ["log"] }
async-graphql = { version = "7", features = ["log", "chrono"] }
async-graphql-axum = "7.0.16"
async-trait = "0.1.88"
axum = { version = "0.8.3", features = ["ws"] }
@@ -26,14 +26,14 @@ build-info = "0.0.42"
cacher = { version = "0.2.0", registry = "xinu" }
chrono = "0.4.40"
clap = { version = "4.5.37", features = ["derive"] }
css-inline = "0.17.0"
css-inline = "0.18.0"
flate2 = "1.1.2"
futures = "0.3.31"
headers = "0.4.0"
html-escape = "0.2.13"
ical = "0.11"
letterbox-notmuch = { path = "../notmuch", version = "0.17.45", registry = "xinu" }
letterbox-shared = { path = "../shared", version = "0.17.45", registry = "xinu" }
letterbox-notmuch = { path = "../notmuch", version = "0.17.47", registry = "xinu" }
letterbox-shared = { path = "../shared", version = "0.17.47", registry = "xinu" }
linkify = "0.10.0"
lol_html = "2.3.0"
mailparse = "0.16.1"
@@ -45,7 +45,7 @@ reqwest = { version = "0.12.15", features = ["blocking"] }
scraper = "0.24.0"
serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.140"
sqlx = { version = "0.8.5", features = ["postgres", "runtime-tokio", "time"] }
sqlx = { version = "0.8.5", features = ["postgres", "runtime-tokio", "chrono"] }
tantivy = { version = "0.25.0", optional = true }
thiserror = "2.0.12"
tokio = "1.44.2"

View File

@@ -0,0 +1,2 @@
-- Add down migration script here
DROP TABLE IF EXISTS snooze;

View File

@@ -0,0 +1,6 @@
-- Add up migration script here
CREATE TABLE IF NOT EXISTS snooze (
id integer NOT NULL GENERATED ALWAYS AS IDENTITY,
message_id text NOT NULL UNIQUE,
wake timestamptz NOT NULL
);

View File

@@ -10,4 +10,4 @@ WHERE
-- TODO remove AND link ~ '^<'
ORDER BY
ROW_NUMBER() OVER (PARTITION BY site ORDER BY date DESC)
LIMIT 100;
LIMIT 1000;

View File

@@ -7,6 +7,7 @@ use async_graphql::{
Union,
};
use cacher::FilesystemCacher;
use chrono::{DateTime, Utc};
use futures::stream;
use letterbox_notmuch::Notmuch;
use serde::{Deserialize, Serialize};
@@ -628,6 +629,42 @@ impl MutationRoot {
nm.tag_remove(&tag, &query)?;
Ok(true)
}
#[instrument(skip_all, fields(query=query, wake_time=wake_time.to_string(), rid=request_id()))]
async fn snooze<'ctx>(
&self,
ctx: &Context<'ctx>,
query: String,
wake_time: DateTime<Utc>,
) -> Result<bool, Error> {
info!("TODO snooze {query} until {wake_time})");
let pool = ctx.data_unchecked::<PgPool>();
sqlx::query!(
r#"
INSERT INTO snooze (message_id, wake)
VALUES ($1, $2)
ON CONFLICT (message_id) DO UPDATE
SET wake = $2
"#,
query,
wake_time
)
.execute(pool)
.await?;
let nm = ctx.data_unchecked::<Notmuch>();
let pool = ctx.data_unchecked::<PgPool>();
#[cfg(feature = "tantivy")]
let tantivy = ctx.data_unchecked::<TantivyConnection>();
let unread = false;
let query: Query = query.parse()?;
newsreader::set_read_status(pool, &query, unread).await?;
#[cfg(feature = "tantivy")]
tantivy.reindex_thread(pool, &query).await?;
nm::set_read_status(nm, &query, unread).await?;
Ok(true)
}
/// Drop and recreate tantivy index. Warning this is slow
#[cfg(feature = "tantivy")]
async fn drop_and_load_index<'ctx>(&self, ctx: &Context<'ctx>) -> Result<bool, Error> {
@@ -648,7 +685,7 @@ impl MutationRoot {
newsreader::refresh(pool, cacher).await?;
// Process email labels
label_unprocessed(&nm, &pool, false, Some(10), "tag:unprocessed").await?;
label_unprocessed(&nm, &pool, false, Some(1000), "tag:unprocessed").await?;
#[cfg(feature = "tantivy")]
{

View File

@@ -19,6 +19,7 @@ use std::{
use async_trait::async_trait;
use cacher::{Cacher, FilesystemCacher};
use chrono::NaiveDateTime;
use css_inline::{CSSInliner, InlineError, InlineOptions};
pub use error::ServerError;
use linkify::{LinkFinder, LinkKind};
@@ -30,7 +31,6 @@ use maplit::{hashmap, hashset};
use regex::Regex;
use reqwest::StatusCode;
use scraper::{Html, Selector};
use sqlx::types::time::PrimitiveDateTime;
use thiserror::Error;
use tracing::{debug, error, info, warn};
use url::Url;
@@ -754,6 +754,7 @@ pub struct Query {
pub is_notmuch: bool,
pub is_newsreader: bool,
pub is_tantivy: bool,
pub is_snoozed: bool,
pub corpus: Option<Corpus>,
}
@@ -777,6 +778,9 @@ impl fmt::Display for Query {
if self.is_newsreader {
write!(f, "is:news ")?;
}
if self.is_snoozed {
write!(f, "is:snoozed ")?;
}
match self.corpus {
Some(c) => write!(f, "corpus:{c:?}")?,
_ => (),
@@ -833,6 +837,7 @@ impl FromStr for Query {
let mut is_notmuch = false;
let mut is_newsreader = false;
let mut is_tantivy = false;
let mut is_snoozed = false;
let mut corpus = None;
for word in s.split_whitespace() {
if word == "is:unread" {
@@ -872,6 +877,8 @@ impl FromStr for Query {
is_newsreader = true;
} else if word == "is:newsreader" {
is_newsreader = true;
} else if word == "is:snoozed" {
is_snoozed = true;
} else {
remainder.push(word.to_string());
}
@@ -890,13 +897,14 @@ impl FromStr for Query {
is_notmuch,
is_newsreader,
is_tantivy,
is_snoozed,
corpus,
})
}
}
pub struct ThreadSummaryRecord {
pub site: Option<String>,
pub date: Option<PrimitiveDateTime>,
pub date: Option<NaiveDateTime>,
pub is_read: Option<bool>,
pub title: Option<String>,
pub uid: String,
@@ -914,11 +922,7 @@ async fn thread_summary_from_row(r: ThreadSummaryRecord) -> ThreadSummary {
title = clean_title(&title).await.expect("failed to clean title");
ThreadSummary {
thread: format!("{NEWSREADER_THREAD_PREFIX}{}", r.uid),
timestamp: r
.date
.expect("post missing date")
.assume_utc()
.unix_timestamp() as isize,
timestamp: r.date.expect("post missing date").and_utc().timestamp() as isize,
date_relative: format!("{:?}", r.date),
//date_relative: "TODO date_relative".to_string(),
matched: 0,

View File

@@ -6,7 +6,7 @@ use letterbox_shared::compute_color;
use maplit::hashmap;
use scraper::Selector;
use sqlx::postgres::PgPool;
use tracing::{error, info, instrument};
use tracing::{error, info, instrument, warn};
use url::Url;
use crate::{
@@ -86,6 +86,10 @@ pub async fn search(
query: &Query,
) -> Result<Vec<(i32, ThreadSummary)>, async_graphql::Error> {
info!("search({after:?} {before:?} {first:?} {last:?} {query:?}");
if query.is_snoozed {
warn!("TODO implement snooze for newsreader::search");
return Ok(Vec::new());
}
if !is_newsreader_query(query) {
return Ok(Vec::new());
}
@@ -211,11 +215,7 @@ pub async fn thread(
}
let title = clean_title(&r.title.unwrap_or("NO TITLE".to_string())).await?;
let is_read = r.is_read.unwrap_or(false);
let timestamp = r
.date
.expect("post missing date")
.assume_utc()
.unix_timestamp();
let timestamp = r.date.expect("post missing date").and_utc().timestamp();
Ok(Thread::News(NewsPost {
thread_id,
is_read,

View File

@@ -64,6 +64,10 @@ pub async fn search(
last: Option<i32>,
query: &Query,
) -> Result<Vec<(i32, ThreadSummary)>, async_graphql::Error> {
if query.is_snoozed {
warn!("TODO implement snooze for nm::search");
return Ok(Vec::new());
}
if !is_notmuch_query(query) {
return Ok(Vec::new());
}

View File

@@ -12,7 +12,7 @@ version.workspace = true
[dependencies]
build-info = "0.0.42"
letterbox-notmuch = { path = "../notmuch", version = "0.17.45", registry = "xinu" }
letterbox-notmuch = { path = "../notmuch", version = "0.17.47", registry = "xinu" }
regex = "1.11.1"
serde = { version = "1.0.219", features = ["derive"] }
sqlx = "0.8.5"

View File

@@ -33,7 +33,7 @@ wasm-bindgen = "=0.2.100"
uuid = { version = "1.16.0", features = [
"js",
] } # direct dep to set js feature, prevents Rng issues
letterbox-shared = { path = "../shared/", version = "0.17.45", registry = "xinu" }
letterbox-shared = { path = "../shared/", version = "0.17.47", registry = "xinu" }
seed_hooks = { version = "0.4.1", registry = "xinu" }
strum_macros = "0.27.1"
gloo-console = "0.3.0"

View File

@@ -51,7 +51,7 @@
},
{
"args": [],
"description": "Indicates that an Input Object is a OneOf Input Object (and thus requires\n exactly one of its field be provided)",
"description": "Indicates that an Input Object is a OneOf Input Object (and thus requires exactly one of its field be provided)",
"locations": [
"INPUT_OBJECT"
],
@@ -107,12 +107,14 @@
}
],
"mutationType": {
"name": "Mutation"
"name": "MutationRoot"
},
"queryType": {
"name": "QueryRoot"
},
"subscriptionType": null,
"subscriptionType": {
"name": "SubscriptionRoot"
},
"types": [
{
"description": null,
@@ -314,6 +316,16 @@
"name": "Corpus",
"possibleTypes": null
},
{
"description": "Implement the DateTime<Utc> scalar\n\nThe input/output is a string in RFC3339 format.",
"enumValues": null,
"fields": null,
"inputFields": null,
"interfaces": null,
"kind": "SCALAR",
"name": "DateTime",
"possibleTypes": null
},
{
"description": null,
"enumValues": [
@@ -969,6 +981,51 @@
}
}
},
{
"args": [
{
"defaultValue": null,
"description": null,
"name": "query",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
}
},
{
"defaultValue": null,
"description": null,
"name": "wakeTime",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
}
}
}
],
"deprecationReason": null,
"description": null,
"isDeprecated": false,
"name": "snooze",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
}
}
},
{
"args": [],
"deprecationReason": null,
@@ -989,7 +1046,7 @@
"inputFields": null,
"interfaces": [],
"kind": "OBJECT",
"name": "Mutation",
"name": "MutationRoot",
"possibleTypes": null
},
{
@@ -1474,6 +1531,33 @@
"name": "String",
"possibleTypes": null
},
{
"description": null,
"enumValues": null,
"fields": [
{
"args": [],
"deprecationReason": null,
"description": null,
"isDeprecated": false,
"name": "values",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
}
}
}
],
"inputFields": null,
"interfaces": [],
"kind": "OBJECT",
"name": "SubscriptionRoot",
"possibleTypes": null
},
{
"description": null,
"enumValues": null,

View File

@@ -0,0 +1,4 @@
mutation SnoozeMutation($query: String!, $wakeTime: DateTime!) {
snooze(query: $query, wakeTime: $wakeTime)
}

View File

@@ -1,4 +1,4 @@
DEV_HOST=localhost
DEV_PORT=9345
graphql-client introspect-schema http://${DEV_HOST:?}:${DEV_PORT:?}/api/graphql --output schema.json
graphql-client introspect-schema http://${DEV_HOST:?}:${DEV_PORT:?}/api/graphql/ --output schema.json
git diff schema.json

View File

@@ -1,7 +1,9 @@
use chrono::Utc;
use gloo_net::{http::Request, Error};
use graphql_client::GraphQLQuery;
use serde::{de::DeserializeOwned, Serialize};
type DateTime = chrono::DateTime<Utc>;
// The paths are relative to the directory where your `Cargo.toml` is located.
// Both json and the GraphQL schema language are supported as sources for the schema
#[derive(GraphQLQuery)]
@@ -52,6 +54,14 @@ pub struct AddTagMutation;
)]
pub struct RemoveTagMutation;
#[derive(GraphQLQuery)]
#[graphql(
schema_path = "graphql/schema.json",
query_path = "graphql/snooze.graphql",
response_derives = "Debug"
)]
pub struct SnoozeMutation;
#[derive(GraphQLQuery)]
#[graphql(
schema_path = "graphql/schema.json",

View File

@@ -1,5 +1,6 @@
use std::collections::HashSet;
use chrono::{DateTime, Utc};
use graphql_client::GraphQLQuery;
use letterbox_shared::WebsocketMessage;
use log::{debug, error, info, warn};
@@ -259,6 +260,29 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
Msg::GoToSearchResults
});
}
Msg::Snooze(query, wake_time) => {
let is_catchup = model.catchup.is_some();
orders.skip().perform_cmd(async move {
let res: Result<
graphql_client::Response<graphql::snooze_mutation::ResponseData>,
gloo_net::Error,
> = send_graphql(graphql::SnoozeMutation::build_query(
graphql::snooze_mutation::Variables {
query: query.clone(),
wake_time,
},
))
.await;
if let Err(e) = res {
error!("Failed to snooze {query} until {wake_time}: {e}");
}
if is_catchup {
Msg::CatchupMarkAsRead
} else {
Msg::GoToSearchResults
}
});
}
Msg::FrontPageRequest {
query,
@@ -813,6 +837,7 @@ pub enum Msg {
SetUnread(String, bool),
AddTag(String, String),
RemoveTag(String, String),
Snooze(String, DateTime<Utc>),
FrontPageRequest {
query: String,

View File

@@ -727,15 +727,19 @@ fn render_open_header(msg: &ShowThreadQueryThreadOnEmailThreadMessages) -> Node<
C!["flex", "p-4", "bg-neutral-800"],
div![avatar],
div![
C!["px-4", "mr-auto"],
span![
C!["font-semibold", "text-sm"],
from_detail.as_ref().map(|addr| attrs! {
At::Title => addr
}),
&from,
" ",
from_detail.as_ref().map(|text| copy_text_widget(&text))
C!["px-4", "flex-1"],
div![
C!["flex"],
div![
C!["font-semibold", "text-sm", "flex-1"],
from_detail.as_ref().map(|addr| attrs! {
At::Title => addr
}),
&from,
" ",
from_detail.as_ref().map(|text| copy_text_widget(&text))
],
snooze_buttons(&id),
],
IF!(!msg.to.is_empty() =>div![
C!["text-xs"],
@@ -1594,9 +1598,13 @@ fn render_news_post_header(post: &ShowThreadQueryThreadOnNewsPost) -> Node<Msg>
C!["flex", "p-4", "bg-neutral-800"],
div![favicon],
div![
C!["px-4", "mr-auto"],
C!["px-4", "mr-auto", "flex-1"],
div![
div![C!["font-semibold", "text-sm"], from],
div![
C!["flex"],
div![C!["font-semibold", "text-sm", "flex-1"], from],
snooze_buttons(&id),
],
div![
C!["flex", "gap-2", "pt-2", "text-sm"],
a![
@@ -1691,3 +1699,45 @@ fn click_to_top() -> Node<Msg> {
ev(Ev::Click, |_| Msg::ScrollToTop)
]
}
fn snooze_buttons(id: &str) -> Node<Msg> {
div![
span![C!["px-2"], ""],
button![
tw_classes::button(),
C!["rounded-r-none"],
"1d",
ev(Ev::Click, {
let id = id.to_string();
move |e| {
e.stop_propagation();
Msg::Snooze(id, Utc::now() + chrono::Days::new(1))
}
})
],
button![
tw_classes::button(),
C!["rounded-none"],
"7d",
ev(Ev::Click, {
let id = id.to_string();
move |e| {
e.stop_propagation();
Msg::Snooze(id, Utc::now() + chrono::Days::new(7))
}
})
],
button![
tw_classes::button(),
C!["rounded-l-none"],
"6m",
ev(Ev::Click, {
let id = id.to_string();
move |e| {
e.stop_propagation();
Msg::Snooze(id, Utc::now() + chrono::Days::new(180))
}
})
],
]
}