From 9cdd5abe2628e6ec3fd036d7894a55b09d622d4d Mon Sep 17 00:00:00 2001 From: Kevin Boos Date: Tue, 24 Sep 2024 18:03:36 +0000 Subject: [PATCH 1/6] Update SDK, use Native sliding sync discovery, don't re-populated event cache initially on EVERY update --- Cargo.lock | 181 ++++++++++++++++---------------------------- Cargo.toml | 6 +- src/sliding_sync.rs | 39 +++++----- 3 files changed, 86 insertions(+), 140 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0f2af4d5..871cef65 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -77,12 +77,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "allocator-api2" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" - [[package]] name = "android-tzdata" version = "0.1.1" @@ -732,11 +726,10 @@ checksum = "0c03c416ed1a30fbb027ef484ba6ab6f80e1eada675e1a2b92fd673c045a1f1d" [[package]] name = "deadpool" -version = "0.10.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb84100978c1c7b37f09ed3ce3e5f843af02c2a2c431bae5b19230dad2c1b490" +checksum = "6541a3916932fe57768d4be0b1ffb5ec7cbf74ca8c903fdfd5c0fe8aa958f0ed" dependencies = [ - "async-trait", "deadpool-runtime", "num_cpus", "tokio", @@ -753,9 +746,9 @@ dependencies = [ [[package]] name = "deadpool-sqlite" -version = "0.7.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8010e36e12f3be22543a5e478b4af20aeead9a700dd69581a5e050a070fc22c" +checksum = "2f9cc6210316f8b7ced394e2a5d2833ce7097fb28afb5881299c61bc18e8e0e9" dependencies = [ "deadpool", "deadpool-sync", @@ -790,22 +783,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" dependencies = [ "const-oid", - "der_derive", - "flagset", "zeroize", ] -[[package]] -name = "der_derive" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fe87ce4529967e0ba1dcf8450bab64d97dfd5010a6256187ffe2e43e6f0e049" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.75", -] - [[package]] name = "deranged" version = "0.3.9" @@ -987,12 +967,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a481586acf778f1b1455424c343f71124b048ffa5f4fc3f8f6ae9dc432dcb3c7" -[[package]] -name = "flagset" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a7e408202050813e6f1d9addadcaafef3dca7530c7ddfb005d4081cce6779" - [[package]] name = "flate2" version = "1.0.28" @@ -1182,19 +1156,18 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.2" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", - "allocator-api2", ] [[package]] name = "hashlink" -version = "0.8.4" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" dependencies = [ "hashbrown", ] @@ -1207,9 +1180,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.3.3" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hilog-sys" @@ -1237,9 +1210,9 @@ dependencies = [ [[package]] name = "html5ever" -version = "0.27.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c13771afe0e6e846f1e67d038d4cb29998a6779f93c809212e4e9c32efd244d4" +checksum = "0ff6858c1f7e2a470c5403091866fa95b36fe0dbac5d771f932c15e5ff1ee501" dependencies = [ "log", "mac", @@ -1445,9 +1418,9 @@ dependencies = [ [[package]] name = "indexed_db_futures" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cc2083760572ee02385ab8b7c02c20925d2dd1f97a1a25a8737a238608f1152" +checksum = "43315957678a70eb21fb0d2384fe86dde0d6c859a01e24ce127eb65a0143d28c" dependencies = [ "accessory", "cfg-if", @@ -1488,9 +1461,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", ] [[package]] @@ -1626,9 +1596,9 @@ dependencies = [ [[package]] name = "libsqlite3-sys" -version = "0.27.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716" +checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f" dependencies = [ "cc", "pkg-config", @@ -2003,9 +1973,9 @@ checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" [[package]] name = "markup5ever" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16ce3abbeba692c8b8441d036ef91aea6df8da2c6b6e21c7e14d3c18e526be45" +checksum = "d581ff8be69d08a2efa23a959d81aa22b739073f749f067348bd4f4ba4b69195" dependencies = [ "log", "phf", @@ -2041,7 +2011,7 @@ dependencies = [ [[package]] name = "matrix-sdk" version = "0.7.1" -source = "git+https://github.com/project-robius/matrix-rust-sdk?branch=re-export-reactions-type#0788b7a5d075ad2521335b625f7fc50b8308c6db" +source = "git+https://github.com/matrix-org/matrix-rust-sdk#5ba90611b43915926a37be83f3de1d4d53bca6bd" dependencies = [ "anymap2", "aquamarine", @@ -2088,7 +2058,7 @@ dependencies = [ [[package]] name = "matrix-sdk-base" version = "0.7.0" -source = "git+https://github.com/project-robius/matrix-rust-sdk?branch=re-export-reactions-type#0788b7a5d075ad2521335b625f7fc50b8308c6db" +source = "git+https://github.com/matrix-org/matrix-rust-sdk#5ba90611b43915926a37be83f3de1d4d53bca6bd" dependencies = [ "as_variant", "async-trait", @@ -2112,13 +2082,12 @@ dependencies = [ [[package]] name = "matrix-sdk-common" version = "0.7.0" -source = "git+https://github.com/project-robius/matrix-rust-sdk?branch=re-export-reactions-type#0788b7a5d075ad2521335b625f7fc50b8308c6db" +source = "git+https://github.com/matrix-org/matrix-rust-sdk#5ba90611b43915926a37be83f3de1d4d53bca6bd" dependencies = [ "async-trait", "futures-core", "futures-util", "gloo-timers", - "instant", "ruma", "serde", "serde_json", @@ -2134,7 +2103,7 @@ dependencies = [ [[package]] name = "matrix-sdk-crypto" version = "0.7.2" -source = "git+https://github.com/project-robius/matrix-rust-sdk?branch=re-export-reactions-type#0788b7a5d075ad2521335b625f7fc50b8308c6db" +source = "git+https://github.com/matrix-org/matrix-rust-sdk#5ba90611b43915926a37be83f3de1d4d53bca6bd" dependencies = [ "aes", "as_variant", @@ -2174,7 +2143,7 @@ dependencies = [ [[package]] name = "matrix-sdk-indexeddb" version = "0.7.0" -source = "git+https://github.com/project-robius/matrix-rust-sdk?branch=re-export-reactions-type#0788b7a5d075ad2521335b625f7fc50b8308c6db" +source = "git+https://github.com/matrix-org/matrix-rust-sdk#5ba90611b43915926a37be83f3de1d4d53bca6bd" dependencies = [ "anyhow", "async-trait", @@ -2202,7 +2171,7 @@ dependencies = [ [[package]] name = "matrix-sdk-sqlite" version = "0.7.1" -source = "git+https://github.com/project-robius/matrix-rust-sdk?branch=re-export-reactions-type#0788b7a5d075ad2521335b625f7fc50b8308c6db" +source = "git+https://github.com/matrix-org/matrix-rust-sdk#5ba90611b43915926a37be83f3de1d4d53bca6bd" dependencies = [ "async-trait", "deadpool-sqlite", @@ -2224,7 +2193,7 @@ dependencies = [ [[package]] name = "matrix-sdk-store-encryption" version = "0.7.0" -source = "git+https://github.com/project-robius/matrix-rust-sdk?branch=re-export-reactions-type#0788b7a5d075ad2521335b625f7fc50b8308c6db" +source = "git+https://github.com/matrix-org/matrix-rust-sdk#5ba90611b43915926a37be83f3de1d4d53bca6bd" dependencies = [ "base64", "blake3", @@ -2243,13 +2212,12 @@ dependencies = [ [[package]] name = "matrix-sdk-ui" version = "0.7.0" -source = "git+https://github.com/project-robius/matrix-rust-sdk?branch=re-export-reactions-type#0788b7a5d075ad2521335b625f7fc50b8308c6db" +source = "git+https://github.com/matrix-org/matrix-rust-sdk#5ba90611b43915926a37be83f3de1d4d53bca6bd" dependencies = [ "as_variant", "async-once-cell", "async-rx", "async-stream", - "async-trait", "async_cell", "chrono", "eyeball", @@ -2272,6 +2240,7 @@ dependencies = [ "serde_json", "thiserror", "tokio", + "tokio-stream", "tracing", "unicode-normalization", ] @@ -2306,13 +2275,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.9" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ + "hermit-abi", "libc", "wasi", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -2620,17 +2590,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "pkcs7" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d79178be066405e0602bf3035946edef6b11b3f9dde46dfe5f8bfd7dea4b77e7" -dependencies = [ - "der", - "spki", - "x509-cert", -] - [[package]] name = "pkcs8" version = "0.10.2" @@ -2756,9 +2715,9 @@ dependencies = [ [[package]] name = "pulldown-cmark" -version = "0.11.2" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4e75767fbc9d92b90e4d0c011f61358cde9513b31ef07ea3631b15ffc3b4fd" +checksum = "666f0f59e259aea2d72e6012290c09877a780935cc3c18b1ceded41f3890d59c" dependencies = [ "bitflags 2.4.1", "memchr", @@ -3080,7 +3039,7 @@ checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" [[package]] name = "ruma" version = "0.10.1" -source = "git+https://github.com/matrix-org/ruma?rev=f25b3220d0c3ece7720020ed180af4955a855402#f25b3220d0c3ece7720020ed180af4955a855402" +source = "git+https://github.com/ruma/ruma?rev=1ae98db9c44f46a590f4c76baf5cef70ebb6970d#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" dependencies = [ "assign", "js_int", @@ -3096,7 +3055,7 @@ dependencies = [ [[package]] name = "ruma-client-api" version = "0.18.0" -source = "git+https://github.com/matrix-org/ruma?rev=f25b3220d0c3ece7720020ed180af4955a855402#f25b3220d0c3ece7720020ed180af4955a855402" +source = "git+https://github.com/ruma/ruma?rev=1ae98db9c44f46a590f4c76baf5cef70ebb6970d#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" dependencies = [ "as_variant", "assign", @@ -3119,7 +3078,7 @@ dependencies = [ [[package]] name = "ruma-common" version = "0.13.0" -source = "git+https://github.com/matrix-org/ruma?rev=f25b3220d0c3ece7720020ed180af4955a855402#f25b3220d0c3ece7720020ed180af4955a855402" +source = "git+https://github.com/ruma/ruma?rev=1ae98db9c44f46a590f4c76baf5cef70ebb6970d#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" dependencies = [ "as_variant", "base64", @@ -3151,7 +3110,7 @@ dependencies = [ [[package]] name = "ruma-events" version = "0.28.1" -source = "git+https://github.com/matrix-org/ruma?rev=f25b3220d0c3ece7720020ed180af4955a855402#f25b3220d0c3ece7720020ed180af4955a855402" +source = "git+https://github.com/ruma/ruma?rev=1ae98db9c44f46a590f4c76baf5cef70ebb6970d#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" dependencies = [ "as_variant", "indexmap", @@ -3176,9 +3135,11 @@ dependencies = [ [[package]] name = "ruma-federation-api" version = "0.9.0" -source = "git+https://github.com/matrix-org/ruma?rev=f25b3220d0c3ece7720020ed180af4955a855402#f25b3220d0c3ece7720020ed180af4955a855402" +source = "git+https://github.com/ruma/ruma?rev=1ae98db9c44f46a590f4c76baf5cef70ebb6970d#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" dependencies = [ + "http", "js_int", + "mime", "ruma-common", "ruma-events", "serde", @@ -3188,7 +3149,7 @@ dependencies = [ [[package]] name = "ruma-html" version = "0.2.0" -source = "git+https://github.com/matrix-org/ruma?rev=f25b3220d0c3ece7720020ed180af4955a855402#f25b3220d0c3ece7720020ed180af4955a855402" +source = "git+https://github.com/ruma/ruma?rev=1ae98db9c44f46a590f4c76baf5cef70ebb6970d#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" dependencies = [ "as_variant", "html5ever", @@ -3200,7 +3161,7 @@ dependencies = [ [[package]] name = "ruma-identifiers-validation" version = "0.9.5" -source = "git+https://github.com/matrix-org/ruma?rev=f25b3220d0c3ece7720020ed180af4955a855402#f25b3220d0c3ece7720020ed180af4955a855402" +source = "git+https://github.com/ruma/ruma?rev=1ae98db9c44f46a590f4c76baf5cef70ebb6970d#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" dependencies = [ "js_int", "thiserror", @@ -3209,8 +3170,9 @@ dependencies = [ [[package]] name = "ruma-macros" version = "0.13.0" -source = "git+https://github.com/matrix-org/ruma?rev=f25b3220d0c3ece7720020ed180af4955a855402#f25b3220d0c3ece7720020ed180af4955a855402" +source = "git+https://github.com/ruma/ruma?rev=1ae98db9c44f46a590f4c76baf5cef70ebb6970d#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" dependencies = [ + "cfg-if", "once_cell", "proc-macro-crate", "proc-macro2", @@ -3223,9 +3185,9 @@ dependencies = [ [[package]] name = "rusqlite" -version = "0.30.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78046161564f5e7cd9008aff3b2990b3850dc8e0349119b98e8f251e099f24d" +checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae" dependencies = [ "bitflags 2.4.1", "fallible-iterator", @@ -3741,26 +3703,25 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.1" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" dependencies = [ "backtrace", "bytes", "libc", "mio", - "num_cpus", "pin-project-lite", "socket2", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", @@ -4110,9 +4071,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.5.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ "getrandom", "wasm-bindgen", @@ -4139,8 +4100,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vodozemac" version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "051d4af70b53b42adf2aac459a305851b8d754f210aaf11ab509e1065beff422" +source = "git+https://github.com/matrix-org/vodozemac?rev=57cbf7e939d7b54d20207e8361b7135bd65c9cc2#57cbf7e939d7b54d20207e8361b7135bd65c9cc2" dependencies = [ "aes", "arrayvec", @@ -4154,7 +4114,6 @@ dependencies = [ "hkdf", "hmac", "matrix-pickle", - "pkcs7", "prost", "rand", "serde", @@ -4194,19 +4153,20 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" dependencies = [ "bumpalo", "log", @@ -4231,9 +4191,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4241,9 +4201,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", @@ -4254,9 +4214,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "wasm-streams" @@ -4610,17 +4570,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "x509-cert" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25eefca1d99701da3a57feb07e5079fc62abba059fc139e98c13bbb250f3ef29" -dependencies = [ - "const-oid", - "der", - "spki", -] - [[package]] name = "xmlwriter" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 02332ef4..ac3f9925 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,10 +30,8 @@ futures-util = "0.3" imbl = { version = "3.0.0", features = ["serde"] } # same as matrix-sdk-ui imghdr = "0.7.0" linkify = "0.10.0" -# matrix-sdk = { git = "https://github.com/matrix-org/matrix-rust-sdk", default-features = false, features = [ "experimental-sliding-sync", "e2e-encryption", "automatic-room-key-forwarding", "markdown", "sqlite", "rustls-tls", "bundled-sqlite" ] } -matrix-sdk = { git = "https://github.com/project-robius/matrix-rust-sdk", branch = "re-export-reactions-type", default-features = false, features = [ "experimental-sliding-sync", "e2e-encryption", "automatic-room-key-forwarding", "markdown", "sqlite", "rustls-tls", "bundled-sqlite" ] } -# matrix-sdk-ui = { git = "https://github.com/matrix-org/matrix-rust-sdk", default-features = false, features = [ "e2e-encryption", "rustls-tls" ] } -matrix-sdk-ui = { git = "https://github.com/project-robius/matrix-rust-sdk", branch = "re-export-reactions-type", default-features = false, features = [ "e2e-encryption", "rustls-tls" ] } +matrix-sdk = { git = "https://github.com/matrix-org/matrix-rust-sdk", default-features = false, features = [ "experimental-sliding-sync", "e2e-encryption", "automatic-room-key-forwarding", "markdown", "sqlite", "rustls-tls", "bundled-sqlite" ] } +matrix-sdk-ui = { git = "https://github.com/matrix-org/matrix-rust-sdk", default-features = false, features = [ "rustls-tls" ] } rangemap = "1.5.0" tokio = { version = "1.33.0", features = ["macros", "rt-multi-thread"] } tracing-subscriber = "0.3.17" diff --git a/src/sliding_sync.rs b/src/sliding_sync.rs index b1ce5b10..fb917a73 100644 --- a/src/sliding_sync.rs +++ b/src/sliding_sync.rs @@ -7,7 +7,7 @@ use makepad_widgets::{error, log, warning, SignalToUI}; use matrix_sdk::{ config::RequestConfig, event_handler::EventHandlerDropGuard, media::MediaRequest, room::RoomMember, ruma::{ api::client::session::get_login_types::v3::LoginType, events::{room::{message::{ForwardThread, RoomMessageEventContent}, MediaSource}, FullStateEventContent, StateEventType}, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedMxcUri, OwnedRoomAliasId, OwnedRoomId, OwnedUserId, UInt, UserId - }, sliding_sync::http::request::ListFilters, Client, Room, SlidingSyncList, SlidingSyncMode + }, sliding_sync::VersionBuilder, Client, Room, SlidingSyncList, SlidingSyncMode }; use matrix_sdk_ui::{timeline::{AnyOtherFullStateEventContent, LiveBackPaginationStatus, RepliedToInfo, TimelineDetails, TimelineItem, TimelineItemContent}, Timeline}; use tokio::{ @@ -61,10 +61,10 @@ async fn login(cli: Cli) -> Result<(Client, Option)> { .map(|h| h.as_str()) .unwrap_or("https://matrix-client.matrix.org/"); // .unwrap_or("https://matrix.org/"); + let mut builder = Client::builder() .homeserver_url(homeserver_url) - // The matrix homeserver's sliding sync proxy doesn't support Simplified MSC3575. - .simplified_sliding_sync(false); + .sliding_sync_version_builder(VersionBuilder::DiscoverNative); if let Some(proxy) = cli.proxy { builder = builder.proxy(proxy); @@ -782,8 +782,8 @@ async fn async_main_loop() -> Result<()> { // Listen for updates to the ignored user list. handle_ignore_user_list_subscriber(client.clone()); - let mut filters = ListFilters::default(); - filters.not_room_types = vec!["m.space".into()]; // Ignore spaces for now. + // let mut filters = ListFilters::default(); + // filters.not_room_types = vec!["m.space".into()]; // Ignore spaces for now. let visible_room_list_name = "VisibleRooms".to_owned(); let visible_room_list = SlidingSyncList::builder(&visible_room_list_name) @@ -791,7 +791,7 @@ async fn async_main_loop() -> Result<()> { // .sync_mode(SlidingSyncMode::new_growing(1)) // only load a few timeline events per room to start with. We'll load more later on demand when a room is first viewed. .timeline_limit(20) - .filters(Some(filters)) + // .filters(Some(filters)) .required_state(vec![ // we want to know immediately: (StateEventType::RoomEncryption, "".to_owned()), // is it encrypted (StateEventType::RoomMember, "$LAZY".to_owned()), // lazily fetch room member profiles for users that have sent events @@ -802,11 +802,9 @@ async fn async_main_loop() -> Result<()> { // (StateEventType::RoomTopic, "".to_owned()), // any topic if known (optional, can be fetched later) ]); - // Now that we're logged in, try to connect to the sliding sync proxy. - log!("Sliding sync proxy URL: {:?}", client.sliding_sync_proxy()); + // Now that we're logged in, try to start up a sliding sync instance. let sliding_sync = client .sliding_sync("main-sync")? - .sliding_sync_proxy("https://slidingsync.lab.matrix.org".try_into()?) .with_all_extensions() // .add_cached_list(visible_room_list).await? .add_list(visible_room_list) @@ -893,15 +891,16 @@ async fn async_main_loop() -> Result<()> { continue; }; - // TODO: when the event cache handles its own cache, we can remove this. - client - .event_cache() - .add_initial_events( - &room_id, - ssroom.timeline_queue().iter().cloned().collect(), - ssroom.prev_batch(), - ) - .await?; + // // TODO: when the event cache handles its own cache, we can remove this. + // TODO FIXME: we should only do this once + // client + // .event_cache() + // .add_initial_events( + // &room_id, + // ssroom.timeline_queue().iter().cloned().collect(), + // ssroom.prev_batch(), + // ) + // .await?; let timeline = Timeline::builder(&room) .track_read_marker_and_receipts() @@ -1123,7 +1122,7 @@ async fn timeline_subscriber_handler( let send_update = |timeline_items: Vector>, changed_indices: Range, clear_cache: bool, num_updates: usize| { if num_updates > 0 { - // log!("timeline_subscriber: applied {num_updates} updates for room {room_id}, timeline now has {} items. Clear cache? {clear_cache}. Changes: {changed_indices:?}.", timeline_items.len()); + log!("timeline_subscriber: applied {num_updates} updates for room {room_id}, timeline now has {} items. Clear cache? {clear_cache}. Changes: {changed_indices:?}.", timeline_items.len()); sender.send(TimelineUpdate::NewItems { items: timeline_items, changed_indices, @@ -1135,7 +1134,7 @@ async fn timeline_subscriber_handler( } }; - const LOG_DIFFS: bool = false; + const LOG_DIFFS: bool = true; while let Some(batch) = subscriber.next().await { let mut num_updates = 0; From 3f48b7bf72c4f53bd7998dd06836c7cd94b7fbcb Mon Sep 17 00:00:00 2001 From: Kevin Boos Date: Thu, 26 Sep 2024 15:18:33 -0700 Subject: [PATCH 2/6] reset LOG_DIFFS to be disabled --- src/sliding_sync.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sliding_sync.rs b/src/sliding_sync.rs index 50e954d1..1aefd2ec 100644 --- a/src/sliding_sync.rs +++ b/src/sliding_sync.rs @@ -1168,9 +1168,13 @@ async fn timeline_subscriber_handler( clear_cache: true, }).expect("Error: timeline update sender couldn't send update with initial items!"); + const LOG_DIFFS: bool = false; + let send_update = |timeline_items: Vector>, changed_indices: Range, clear_cache: bool, num_updates: usize| { if num_updates > 0 { - log!("timeline_subscriber: applied {num_updates} updates for room {room_id}, timeline now has {} items. Clear cache? {clear_cache}. Changes: {changed_indices:?}.", timeline_items.len()); + if LOG_DIFFS { + log!("timeline_subscriber: applied {num_updates} updates for room {room_id}, timeline now has {} items. Clear cache? {clear_cache}. Changes: {changed_indices:?}.", timeline_items.len()); + } sender.send(TimelineUpdate::NewItems { items: timeline_items, changed_indices, @@ -1182,8 +1186,6 @@ async fn timeline_subscriber_handler( } }; - const LOG_DIFFS: bool = true; - while let Some(batch) = subscriber.next().await { let mut num_updates = 0; let mut index_of_first_change = usize::MAX; From 3897b0c065346a5f4f003665cb8c4b299a3894aa Mon Sep 17 00:00:00 2001 From: Kevin Boos Date: Sun, 29 Sep 2024 23:27:26 -0700 Subject: [PATCH 3/6] Migrate to the new simplified SyncService with RoomListService currently untested but the implementation is mostly complete. --- src/home/rooms_list.rs | 36 ++- src/persistent_state.rs | 4 +- src/sliding_sync.rs | 617 +++++++++++++++++++++++----------------- 3 files changed, 385 insertions(+), 272 deletions(-) diff --git a/src/home/rooms_list.rs b/src/home/rooms_list.rs index 3990d631..a4f31197 100644 --- a/src/home/rooms_list.rs +++ b/src/home/rooms_list.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use crossbeam_queue::SegQueue; use makepad_widgets::*; -use matrix_sdk::ruma::{OwnedRoomId, MilliSecondsSinceUnixEpoch}; +use matrix_sdk::{ruma::{MilliSecondsSinceUnixEpoch, OwnedRoomId}, Room}; use super::room_preview::RoomPreviewAction; @@ -66,8 +66,15 @@ live_design! { /// (which is called from background async tasks that receive updates from the matrix server), /// and then dequeued by the `RoomsList` widget's `handle_event` function. pub enum RoomsListUpdate { + /// No rooms have been loaded yet. + NotLoaded, + /// Some rooms were loaded, and the server optionally told us + /// the max number of rooms that will ever be loaded. + LoadedRooms{ max_rooms: Option }, /// Add a new room to the list of all rooms. AddRoom(RoomPreviewEntry), + /// Clear all rooms in the list of all rooms. + ClearRooms, /// Update the latest event content and timestamp for the given room. UpdateLatestEvent { room_id: OwnedRoomId, @@ -86,7 +93,6 @@ pub enum RoomsListUpdate { avatar: RoomPreviewAvatar, }, /// Remove the given room from the list of all rooms. - #[allow(unused)] RemoveRoom(OwnedRoomId), /// Update the status label at the bottom of the list of all rooms. Status { @@ -120,7 +126,7 @@ pub enum RoomListAction { #[derive(Debug, Default)] pub struct RoomPreviewEntry { /// The matrix ID of this room. - pub room_id: Option, + pub room_id: OwnedRoomId, /// The displayable name of this room, if known. pub room_name: Option, /// The timestamp and Html text content of the latest message in this room. @@ -149,6 +155,8 @@ pub struct RoomsList { #[deref] view: View, /// The list of all known rooms and their cached preview info. + // + // TODO: change this into a hashmap keyed by room ID. #[rust] all_rooms: Vec, /// Maps the WidgetUid of a `RoomPreview` to that room's index in the `all_rooms` vector. #[rust] rooms_list_map: HashMap, @@ -156,6 +164,18 @@ pub struct RoomsList { #[rust] status: String, /// The index of the currently selected room #[rust] current_active_room_index: Option, + /// The maximum number of rooms that will ever be loaded. + #[rust] max_known_rooms: Option, +} + +impl RoomsList { + fn update_status_rooms_count(&mut self) { + self.status = if let Some(max_rooms) = self.max_known_rooms { + format!("Loaded {} of {} total rooms.", self.all_rooms.len(), max_rooms) + } else { + format!("Loaded {} rooms.", self.all_rooms.len()) + }; + } } impl Widget for RoomsList { @@ -197,6 +217,16 @@ impl Widget for RoomsList { error!("Error: couldn't find room {room_id} to remove room"); } } + RoomsListUpdate::ClearRooms => { + self.all_rooms.clear(); + } + RoomsListUpdate::NotLoaded => { + self.status = "Loading rooms (waiting for homeserver)...".to_string(); + } + RoomsListUpdate::LoadedRooms { max_rooms } => { + self.max_known_rooms = max_rooms; + self.update_status_rooms_count(); + } RoomsListUpdate::Status { status } => { self.status = status; } diff --git a/src/persistent_state.rs b/src/persistent_state.rs index 71edd5ed..75d996e9 100644 --- a/src/persistent_state.rs +++ b/src/persistent_state.rs @@ -1,8 +1,6 @@ //! Handles app persistence by saving and restoring client session data to/from the filesystem. -use std::{ - path::PathBuf, -}; +use std::path::PathBuf; use anyhow::{anyhow, bail}; use makepad_widgets::log; use matrix_sdk::{ diff --git a/src/sliding_sync.rs b/src/sliding_sync.rs index 1aefd2ec..f8680f58 100644 --- a/src/sliding_sync.rs +++ b/src/sliding_sync.rs @@ -1,5 +1,6 @@ use anyhow::{Result, bail}; use clap::Parser; +use eyeball::Subscriber; use eyeball_im::VectorDiff; use futures_util::{pin_mut, StreamExt}; use imbl::Vector; @@ -9,7 +10,7 @@ use matrix_sdk::{ api::client::session::get_login_types::v3::LoginType, events::{room::{message::{ForwardThread, RoomMessageEventContent}, MediaSource}, FullStateEventContent, StateEventType}, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedMxcUri, OwnedRoomAliasId, OwnedRoomId, OwnedUserId, UInt, UserId }, sliding_sync::VersionBuilder, Client, Room, SlidingSyncList, SlidingSyncMode }; -use matrix_sdk_ui::{timeline::{AnyOtherFullStateEventContent, LiveBackPaginationStatus, RepliedToInfo, TimelineDetails, TimelineItem, TimelineItemContent}, Timeline}; +use matrix_sdk_ui::{room_list_service::{self, RoomListLoadingState}, sync_service::{self, SyncService}, timeline::{AnyOtherFullStateEventContent, EventTimelineItem, LiveBackPaginationStatus, RepliedToInfo, TimelineDetails, TimelineItem, TimelineItemContent}, RoomListService, Timeline}; use tokio::{ runtime::Handle, sync::mpsc::{UnboundedSender, UnboundedReceiver}, task::JoinHandle, @@ -719,6 +720,14 @@ pub fn get_client() -> Option { CLIENT.get().cloned() } +/// The singleton sync service. +static SYNC_SERVICE: OnceLock = OnceLock::new(); + +pub fn get_sync_service() -> Option<&SyncService> { + SYNC_SERVICE.get() +} + + /// The list of users that the current user has chosen to ignore. /// Ideally we shouldn't have to maintain this list ourselves, /// but the Matrix SDK doesn't currently properly maintain the list of ignored users. @@ -846,263 +855,202 @@ async fn async_main_loop() -> Result<()> { // Listen for updates to the ignored user list. handle_ignore_user_list_subscriber(client.clone()); - // let mut filters = ListFilters::default(); - // filters.not_room_types = vec!["m.space".into()]; // Ignore spaces for now. - - let visible_room_list_name = "VisibleRooms".to_owned(); - let visible_room_list = SlidingSyncList::builder(&visible_room_list_name) - .sync_mode(SlidingSyncMode::new_paging(5).maximum_number_of_rooms_to_fetch(50)) - // .sync_mode(SlidingSyncMode::new_growing(1)) - // only load a few timeline events per room to start with. We'll load more later on demand when a room is first viewed. - .timeline_limit(20) - // .filters(Some(filters)) - .required_state(vec![ // we want to know immediately: - (StateEventType::RoomEncryption, "".to_owned()), // is it encrypted - (StateEventType::RoomMember, "$LAZY".to_owned()), // lazily fetch room member profiles for users that have sent events - (StateEventType::RoomMember, "$ME".to_owned()), // fetch profile for "me", the currently logged-in user (optional) - (StateEventType::RoomCreate, "".to_owned()), // room creation type - (StateEventType::RoomName, "".to_owned()), // the room's displayable name - (StateEventType::RoomAvatar, "".to_owned()), // avatar if set - // (StateEventType::RoomTopic, "".to_owned()), // any topic if known (optional, can be fetched later) - ]); - - // Now that we're logged in, try to start up a sliding sync instance. - let sliding_sync = client - .sliding_sync("main-sync")? - .with_all_extensions() - // .add_cached_list(visible_room_list).await? - .add_list(visible_room_list) + let sync_service = SyncService::builder(client.clone()) .build() .await?; + sync_service.start().await; + handle_sync_service_state_subscriber(sync_service.state()); + let room_list_service = sync_service.room_list_service(); + SYNC_SERVICE.set(sync_service).expect("BUG: SYNC_SERVICE already set!"); + + let all_rooms_list = room_list_service.all_rooms().await?; + handle_room_list_service(all_rooms_list.loading_state()); + let (known_rooms, room_diff_stream) = all_rooms_list.entries(); + for room in known_rooms.iter() { + add_new_room(room); + } + let mut all_known_rooms = known_rooms; + const LOG_ROOM_LIST_DIFFS: bool = true; - // let active_room_list_name = "ActiveRoom".to_owned(); - // let active_room_list = SlidingSyncList::builder(&active_room_list_name) - // .sync_mode(SlidingSyncMode::new_paging(10)) - // .timeline_limit(u32::MAX) - // .required_state(vec![ // we want to know immediately: - // (StateEventType::RoomEncryption, "".to_owned()), // is it encrypted - // (StateEventType::RoomTopic, "".to_owned()), // any topic if known - // (StateEventType::RoomAvatar, "".to_owned()), // avatar if set - // ]); - // sliding_sync.add_cached_list(active_room_list).await?; - - - let stream = sliding_sync.sync(); - pin_mut!(stream); - - let mut stream_error = None; - loop { - let update = match stream.next().await { - Some(Ok(u)) => { - let curr = start.elapsed().as_secs_f64(); - log!("{curr:>8.2} Received an update. Summary: {u:?}"); - // log!(" --> Current room list: {:?}", sliding_sync.get_all_rooms().await.into_iter().map(|r| r.room_id().to_owned()).collect::>()); - u - } - Some(Err(e)) => { - error!("sync loop was stopped by client error processing: {e}"); - stream_error = Some(e); - break; - } - None => { - error!("sync loop ended unexpectedly"); - break; - } - }; - - for room_id in update.rooms { - let Some(room) = client.get_room(&room_id) else { - error!("Error: couldn't get Room {room_id:?} that had an update"); - continue - }; - let room_name = room.compute_display_name().await; - - log!("\n{room_id:?} --> {:?} has an update - display_name: {:?}, - topic: {:?}, - is_synced: {:?}, is_state_fully_synced: {:?}, - is_space: {:?}, - create: {:?}, - canonical-alias: {:?}, - alt_aliases: {:?}, - guest_access: {:?}, - history_visibility: {:?}, - is_public: {:?}, - join_rule: {:?}, - latest_event: {:?} - ", - room.name(), - room_name, - room.topic(), - room.is_synced(), room.is_state_fully_synced(), - room.is_space(), - room.create_content(), - room.canonical_alias(), - room.alt_aliases(), - room.guest_access(), - room.history_visibility(), - room.is_public(), - room.join_rule(), - room.latest_event(), - ); - - // sliding_sync.subscribe_to_room(room_id.to_owned(), None); - // log!(" --> Subscribing to above room {:?}", room_id); - - let timeline = Timeline::builder(&room) - .track_read_marker_and_receipts() - .build() - .await?; - - let latest_tl = timeline.latest_event().await; - - let mut room_exists = false; - let mut room_name_changed = None; - let mut latest_event_changed = None; - let mut room_avatar_changed = false; - - if let Some(existing) = ALL_ROOM_INFO.lock().unwrap().get_mut(&room_id) { - room_exists = true; - - // Obtain the details of any changes to this room based on this its latest timeline event. - if let Some(event_tl_item) = &latest_tl { - let timestamp = event_tl_item.timestamp(); - if timestamp > existing.latest_event_timestamp { - existing.latest_event_timestamp = timestamp; - let latest_event_sender_username = match event_tl_item.sender_profile() { - TimelineDetails::Ready(profile) => profile.display_name.as_deref(), - TimelineDetails::Unavailable => { - if let Some(event_id) = event_tl_item.event_id() { - submit_async_request(MatrixRequest::FetchDetailsForEvent { - room_id: room_id.to_owned(), - event_id: event_id.to_owned(), - }); - } - None - } - _ => None, - } - .unwrap_or_else(|| event_tl_item.sender().as_str()); - - latest_event_changed = Some(( - timestamp, - text_preview_of_timeline_item( - event_tl_item.content(), - latest_event_sender_username, - ).format_with(latest_event_sender_username), - )); - - match event_tl_item.content() { - TimelineItemContent::OtherState(other) => match other.content() { - AnyOtherFullStateEventContent::RoomName(FullStateEventContent::Original { content, .. }) => { - room_name_changed = Some(content.name.clone()); - } - AnyOtherFullStateEventContent::RoomAvatar(_avatar_event) => { - room_avatar_changed = true; // we'll fetch the avatar later. - } - _ => { } - } - _ => { } + while let Some(batch) = room_diff_stream.next().await { + for diff in batch { + match diff { + VectorDiff::Append { values: new_rooms } => { + let _num_new_rooms = new_rooms.len(); + if LOG_ROOM_LIST_DIFFS { log!("room_list: diff Append {_num_new_rooms}"); } + for new_room in &new_rooms { + add_new_room(&new_room).await?; + } + all_known_rooms.append(new_rooms); + } + VectorDiff::Clear => { + if LOG_ROOM_LIST_DIFFS { log!("room_list: diff Clear"); } + all_known_rooms.clear(); + ALL_ROOM_INFO.lock().unwrap().clear(); + // TODO: we probably need to remove each room individually to kill off + // all of the async tasks associated with them (i.e., the timeline subscriber). + // Or, better yet, implement the drop handler for RoomInfo to do so. + enqueue_rooms_list_update(RoomsListUpdate::ClearRooms); + } + VectorDiff::PushFront { value: new_room } => { + if LOG_ROOM_LIST_DIFFS { log!("room_list: diff PushFront"); } + add_new_room(&new_room).await?; + all_known_rooms.push_front(new_room); + } + VectorDiff::PushBack { value: new_room } => { + if LOG_ROOM_LIST_DIFFS { log!("room_list: diff PushBack"); } + add_new_room(&new_room).await?; + all_known_rooms.push_back(new_room); + } + VectorDiff::PopFront => { + if LOG_ROOM_LIST_DIFFS { log!("room_list: diff PopFront"); } + if let Some(room) = all_known_rooms.pop_front() { + remove_room(room); + } + } + VectorDiff::PopBack => { + if LOG_ROOM_LIST_DIFFS { log!("room_list: diff PopBack"); } + if let Some(room) = all_known_rooms.pop_back() { + remove_room(room); + } + } + VectorDiff::Insert { index, value: new_room } => { + if LOG_ROOM_LIST_DIFFS { log!("room_list: diff Insert at {index}"); } + add_new_room(&new_room).await?; + all_known_rooms.insert(index, new_room); + num_updates += 1; + } + VectorDiff::Set { index, value: changed_room } => { + if LOG_ROOM_LIST_DIFFS { log!("room_list: diff Set at {index} !!!!!!!!"); } + update_room(&changed_room).await?; + all_known_rooms.set(index, changed_room); + } + VectorDiff::Remove { index } => { + if LOG_ROOM_LIST_DIFFS { log!("room_list: diff Remove at {index}"); } + if let Some(room) = all_known_rooms.remove(index) { + remove_room(room); + } + } + VectorDiff::Truncate { length } => { + if LOG_ROOM_LIST_DIFFS { log!("room_list: diff Truncate to {length}"); } + while all_known_rooms.len() > length { + if let Some(room) = all_known_rooms.pop_back() { + remove_room(room); } } + all_known_rooms.truncate(length); // sanity check + } + VectorDiff::Reset { values } => { + // We implement this by clearing all rooms and then adding back the new values. + if LOG_ROOM_LIST_DIFFS { log!("room_list: diff Reset, new length {values.len()}"); } + all_known_rooms = values; + ALL_ROOM_INFO.lock().unwrap().clear(); + // TODO: we probably need to remove each room individually to kill off + // all of the async tasks associated with them (i.e., the timeline subscriber). + // Or, better yet, implement the drop handler for RoomInfo to do so. + enqueue_rooms_list_update(RoomsListUpdate::ClearRooms); + for room in &all_known_rooms { + add_new_room(&room).await?; + } } } + } + } - // Send an update to the rooms_list if the latest event for this room has changed. - if let Some((timestamp, latest_message_text)) = latest_event_changed { - rooms_list::enqueue_rooms_list_update(RoomsListUpdate::UpdateLatestEvent { - room_id: room_id.clone(), - timestamp, - latest_message_text, - }); - } + bail!("room list service sync loop ended unexpectedly") +} - // Send an update to the rooms_list if the room name has changed. - if let Some(new_room_name) = room_name_changed { - rooms_list::enqueue_rooms_list_update(RoomsListUpdate::UpdateRoomName { - room_id: room_id.clone(), - new_room_name, - }); - } - let room_name_str = room_name.ok().map(|n| n.to_string()); +/// Invoked when the room list service has received an update that changes an existing room. +async fn update_room(room: &room_list_service::Room) -> matrix_sdk::Result<()> { + todo!("update_room") +} - // Handle an entirely new room that we haven't seen before. - if !room_exists { - // Indicate that we'll fetch the avatar for this new room later. - room_avatar_changed = true; - let (timeline_update_sender, timeline_update_receiver) = crossbeam_channel::unbounded(); - let tl_arc = Arc::new(timeline); - - Handle::current().spawn(timeline_subscriber_handler( - room_id.clone(), - tl_arc.clone(), - timeline_update_sender.clone(), - )); - rooms_list::enqueue_rooms_list_update(RoomsListUpdate::AddRoom(RoomPreviewEntry { - room_id: Some(room_id.clone()), - latest: latest_tl.as_ref().map(|ev| { - let sender_username = match ev.sender_profile() { - TimelineDetails::Ready(profile) => profile.display_name.as_deref(), - _ => None, - }.unwrap_or_else(|| ev.sender().as_str()); - ( - ev.timestamp(), - text_preview_of_timeline_item(ev.content(), sender_username) - .format_with(sender_username), - ) - }), - avatar: avatar_from_room_name(room_name_str.as_deref().unwrap_or_default()), - room_name: room_name_str.clone(), - ..RoomPreviewEntry::default() - })); - - ALL_ROOM_INFO.lock().unwrap().insert( - room_id.clone(), - RoomInfo { - room_id: room_id.clone(), - latest_event_timestamp: latest_tl.as_ref() - .map(|ev| ev.timestamp()) - .unwrap_or(MilliSecondsSinceUnixEpoch(UInt::MIN)), - timeline: tl_arc, - timeline_update_receiver: Some(timeline_update_receiver), - timeline_update_sender, - pagination_status_task: None, - typing_notice_subscriber: None, - }, - ); - } +/// Invoked when the room list service has received an update to remove an existing room. +fn remove_room(room: room_list_service::Room) { + ALL_ROOM_INFO.lock().unwrap().remove(room.room_id()); + enqueue_rooms_list_update( + RoomsListUpdate::RemoveRoom(room.room_id().to_owned()) + ); + // TODO: we probably need to kill all of the async tasks associated + // with this room (i.e., the timeline subscriber. etc). + // Or, better yet, implement `RoomInfo::drop()` to do so. +} - // Send an update to the rooms_list if the room avatar has changed, - // which is done within an async task because we need to fetch the avatar from the server. - // This must be done here because we can't hold the lock on ALL_ROOM_INFO across an `.await` call, - // and we can't send an `UpdateRoomAvatar` update until after the `AddRoom` update has been sent. - if room_avatar_changed { - Handle::current().spawn(async move { - let avatar = room_avatar(&room, &room_name_str).await; - rooms_list::enqueue_rooms_list_update(RoomsListUpdate::UpdateRoomAvatar { - room_id, - avatar, + +/// Invoked when the room list service has received an update with a brand new room. +async fn add_new_room(room: &room_list_service::Room) -> matrix_sdk::Result<()> { + let room_avatar_changed = true; + let room_id = room.room_id().to_owned(); + + let timeline = room.default_room_timeline_builder().await? + .track_read_marker_and_receipts() + .build() + .await?; + + let latest_event = timeline.latest_event().await; + + let (timeline_update_sender, timeline_update_receiver) = crossbeam_channel::unbounded(); + let tl_arc = Arc::new(timeline); + + let room_name = room.compute_display_name().await + .map(|n| n.to_string()) + .ok(); + + Handle::current().spawn(timeline_subscriber_handler( + room.inner_room().clone(), + tl_arc.clone(), + timeline_update_sender.clone(), + )); + + let latest = latest_event.as_ref().map(|ev| { + let sender_username = match ev.sender_profile() { + TimelineDetails::Ready(profile) => profile.display_name.as_deref(), + TimelineDetails::Unavailable => { + if let Some(event_id) = ev.event_id() { + submit_async_request(MatrixRequest::FetchDetailsForEvent { + room_id: room_id.clone(), + event_id: event_id.to_owned(), }); - }); + } + None } - } - - if !update.lists.is_empty() { - log!("Lists have an update: {:?}", update.lists); - } - } + _ => None, + }.unwrap_or_else(|| ev.sender().as_str()); + ( + ev.timestamp(), + text_preview_of_timeline_item(ev.content(), sender_username) + .format_with(sender_username), + ) + }); - if let Some(e) = stream_error { - bail!(e) - } else { - bail!("sync loop ended unexpectedly") - } + rooms_list::enqueue_rooms_list_update(RoomsListUpdate::AddRoom(RoomPreviewEntry { + room_id: room_id.clone(), + latest, + // start with a basic text avatar; the avatar image will be fetched asynchronously later. + avatar: avatar_from_room_name(room_name.as_deref().unwrap_or_default()), + room_name, + ..RoomPreviewEntry::default() + })); + + ALL_ROOM_INFO.lock().unwrap().insert( + room_id.clone(), + RoomInfo { + room_id, + latest_event_timestamp: latest_event.as_ref() + .map(|ev| ev.timestamp()) + .unwrap_or(MilliSecondsSinceUnixEpoch(UInt::MIN)), + timeline: tl_arc, + timeline_update_receiver: Some(timeline_update_receiver), + timeline_update_sender, + pagination_status_task: None, + typing_notice_subscriber: None, + }, + ); } - #[allow(unused)] async fn current_ignore_user_list(client: &Client) -> Option> { use matrix_sdk::ruma::events::ignored_user_list::IgnoredUserListEventContent; @@ -1119,7 +1067,6 @@ async fn current_ignore_user_list(client: &Client) -> Option) { + Handle::current().spawn(async move { + while let Some(state) = subsriber.next().await { + log!("Received a sync service state update: {state:?}"); + if state == sync_service::State::Error { + log!("Restarting sync service due to error."); + if let Some(ss) = SYNC_SERVICE.get() { + ss.start().await; + } + } + } + }); +} + + +fn handle_room_list_service_loading_state(loading_state: Subscriber) { + Handle::current().spawn(async move { + while let Some(loading_state) = loading_state.next().await { + match loading_state { + RoomListLoadingState::NotLoaded => { + enqueue_rooms_list_update(RoomsListUpdate::NotLoaded); + } + RoomListLoadingState::Loaded { maximum_number_of_rooms } => { + enqueue_rooms_list_update(RoomsListUpdate::LoadedRooms { max_rooms: maximum_number_of_rooms }); + } + } + } + }); +} + + async fn timeline_subscriber_handler( - room_id: OwnedRoomId, + room: Room, timeline: Arc, sender: crossbeam_channel::Sender, ) { + let room_id = room.room_id(); log!("Starting timeline subscriber for room {room_id}..."); let (mut timeline_items, mut subscriber) = timeline.subscribe_batched().await; log!("Received initial timeline update for room {room_id}."); @@ -1168,26 +1147,53 @@ async fn timeline_subscriber_handler( clear_cache: true, }).expect("Error: timeline update sender couldn't send update with initial items!"); - const LOG_DIFFS: bool = false; + const LOG_TIMELINE_DIFFS: bool = false; - let send_update = |timeline_items: Vector>, changed_indices: Range, clear_cache: bool, num_updates: usize| { - if num_updates > 0 { - if LOG_DIFFS { - log!("timeline_subscriber: applied {num_updates} updates for room {room_id}, timeline now has {} items. Clear cache? {clear_cache}. Changes: {changed_indices:?}.", timeline_items.len()); - } - sender.send(TimelineUpdate::NewItems { - items: timeline_items, - changed_indices, - clear_cache, - }).expect("Error: timeline update sender couldn't send update with new items!"); + let mut latest_event = timeline.latest_event().await; - // Send a Makepad-level signal to update this room's timeline UI view. - SignalToUI::set_ui_signal(); + let room_id2 = room_id.clone(); + let send_update = | + timeline_items: Vector>, + changed_indices: Range, + clear_cache: bool, + num_updates: usize, + new_latest_event: Option, + | { + if LOG_TIMELINE_DIFFS { + log!("timeline_subscriber: applied {num_updates} updates for room {room_id}, timeline now has {} items. Clear cache? {clear_cache}. Changes: {changed_indices:?}.", timeline_items.len()); + } + sender.send(TimelineUpdate::NewItems { + items: timeline_items, + changed_indices, + clear_cache, + }).expect("Error: timeline update sender couldn't send update with new items!"); + + // Send a Makepad-level signal to update this room's timeline UI view. + SignalToUI::set_ui_signal(); + + // Update the latest event for this room. + if let Some(new_latest) = new_latest_event { + if latest_event.is_some_and(|ev| ev.timestamp() < new_latest.timestamp()) { + let room_avatar_changed = update_latest_event(room_id2, new_latest); + latest_event = Some(new_latest_event); + if room_avatar_changed { + // Spawn a new async task to fetch the room's new avatar. + let room_name_str = room.cached_display_name(); + Handle::current().spawn(async move { + let avatar = room_avatar(&inner_room, &room_name_str).await; + rooms_list::enqueue_rooms_list_update(RoomsListUpdate::UpdateRoomAvatar { + room_id, + avatar, + }); + }); + } + } } }; while let Some(batch) = subscriber.next().await { let mut num_updates = 0; + let mut reobtain_latest_event = false; let mut index_of_first_change = usize::MAX; let mut index_of_last_change = usize::MIN; let mut clear_cache = false; // whether to clear the entire cache of items @@ -1198,39 +1204,45 @@ async fn timeline_subscriber_handler( index_of_first_change = min(index_of_first_change, timeline_items.len()); timeline_items.extend(values); index_of_last_change = max(index_of_last_change, timeline_items.len()); - if LOG_DIFFS { log!("timeline_subscriber: diff Append {_values_len}. Changes: {index_of_first_change}..{index_of_last_change}"); } + if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: diff Append {_values_len}. Changes: {index_of_first_change}..{index_of_last_change}"); } + reobtain_latest_event = true; num_updates += 1; } VectorDiff::Clear => { - if LOG_DIFFS { log!("timeline_subscriber: diff Clear"); } + if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: diff Clear"); } clear_cache = true; timeline_items.clear(); + reobtain_latest_event = true; num_updates += 1; } VectorDiff::PushFront { value } => { - if LOG_DIFFS { log!("timeline_subscriber: diff PushFront"); } + if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: diff PushFront"); } clear_cache = true; timeline_items.push_front(value); + reobtain_latest_event |= latest_event.is_none(); num_updates += 1; } VectorDiff::PushBack { value } => { index_of_first_change = min(index_of_first_change, timeline_items.len()); timeline_items.push_back(value); index_of_last_change = max(index_of_last_change, timeline_items.len()); - if LOG_DIFFS { log!("timeline_subscriber: diff PushBack. Changes: {index_of_first_change}..{index_of_last_change}"); } + if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: diff PushBack. Changes: {index_of_first_change}..{index_of_last_change}"); } + reobtain_latest_event = true; num_updates += 1; } VectorDiff::PopFront => { - if LOG_DIFFS { log!("timeline_subscriber: diff PopFront"); } + if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: diff PopFront"); } clear_cache = true; timeline_items.pop_front(); + // This doesn't affect whether we should reobtain the latest event. num_updates += 1; } VectorDiff::PopBack => { timeline_items.pop_back(); index_of_first_change = min(index_of_first_change, timeline_items.len()); index_of_last_change = usize::MAX; - if LOG_DIFFS { log!("timeline_subscriber: diff PopBack. Changes: {index_of_first_change}..{index_of_last_change}"); } + if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: diff PopBack. Changes: {index_of_first_change}..{index_of_last_change}"); } + reobtain_latest_event = true; num_updates += 1; } VectorDiff::Insert { index, value } => { @@ -1241,14 +1253,16 @@ async fn timeline_subscriber_handler( index_of_last_change = usize::MAX; } timeline_items.insert(index, value); - if LOG_DIFFS { log!("timeline_subscriber: diff Insert at {index}. Changes: {index_of_first_change}..{index_of_last_change}"); } + if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: diff Insert at {index}. Changes: {index_of_first_change}..{index_of_last_change}"); } + reobtain_latest_event = true; num_updates += 1; } VectorDiff::Set { index, value } => { index_of_first_change = min(index_of_first_change, index); index_of_last_change = max(index_of_last_change, index.saturating_add(1)); timeline_items.set(index, value); - if LOG_DIFFS { log!("timeline_subscriber: diff Set at {index}. Changes: {index_of_first_change}..{index_of_last_change}"); } + if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: diff Set at {index}. Changes: {index_of_first_change}..{index_of_last_change}"); } + reobtain_latest_event = true; num_updates += 1; } VectorDiff::Remove { index } => { @@ -1259,7 +1273,8 @@ async fn timeline_subscriber_handler( index_of_last_change = usize::MAX; } timeline_items.remove(index); - if LOG_DIFFS { log!("timeline_subscriber: diff Remove at {index}. Changes: {index_of_first_change}..{index_of_last_change}"); } + if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: diff Remove at {index}. Changes: {index_of_first_change}..{index_of_last_change}"); } + reobtain_latest_event = true; num_updates += 1; } VectorDiff::Truncate { length } => { @@ -1270,24 +1285,94 @@ async fn timeline_subscriber_handler( index_of_last_change = usize::MAX; } timeline_items.truncate(length); - if LOG_DIFFS { log!("timeline_subscriber: diff Truncate to length {length}. Changes: {index_of_first_change}..{index_of_last_change}"); } + if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: diff Truncate to length {length}. Changes: {index_of_first_change}..{index_of_last_change}"); } + reobtain_latest_event = true; num_updates += 1; } VectorDiff::Reset { values } => { - if LOG_DIFFS { log!("timeline_subscriber: diff Reset, new length {}", values.len()); } + if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: diff Reset, new length {}", values.len()); } clear_cache = true; // we must assume all items have changed. timeline_items = values; + reobtain_latest_event = true; num_updates += 1; } } } - send_update(timeline_items.clone(), index_of_first_change..index_of_last_change, clear_cache, num_updates); + + if num_updates > 0 { + let new_latest_event = if reobtain_latest_event { + timeline.latest_event().await + } else { + None + }; + send_update( + timeline_items.clone(), + index_of_first_change..index_of_last_change, + clear_cache, + num_updates, + new_latest_event, + ); + } } error!("Error: unexpectedly ended timeline subscriber for room {room_id}."); } +/// Updates the latest event for the given room. +/// +/// Returns `true` if this latest event indicates that the room's avatar has changed +/// and should also be updated. +fn update_latest_event( + room_id: OwnedRoomId, + event_tl_item: EventTimelineItem, +) -> bool { + let mut room_avatar_changed = false; + + let latest_event_sender_username = match event_tl_item.sender_profile() { + TimelineDetails::Ready(profile) => profile.display_name.as_deref(), + TimelineDetails::Unavailable => { + if let Some(event_id) = event_tl_item.event_id() { + submit_async_request(MatrixRequest::FetchDetailsForEvent { + room_id: room_id.to_owned(), + event_id: event_id.to_owned(), + }); + } + None + } + _ => None, + } + .unwrap_or_else(|| event_tl_item.sender().as_str()); + + let latest_message_text = text_preview_of_timeline_item( + event_tl_item.content(), + latest_event_sender_username, + ).format_with(latest_event_sender_username); + + // Check for relevant state events: a changed room name or avatar. + match event_tl_item.content() { + TimelineItemContent::OtherState(other) => match other.content() { + AnyOtherFullStateEventContent::RoomName(FullStateEventContent::Original { content, .. }) => { + rooms_list::enqueue_rooms_list_update(RoomsListUpdate::UpdateRoomName { + room_id: room_id.clone(), + new_room_name: content.name.clone(), + }); + } + AnyOtherFullStateEventContent::RoomAvatar(_avatar_event) => { + room_avatar_changed = true; + } + _ => { } + } + _ => { } + } + enqueue_rooms_list_update(RoomsListUpdate::UpdateLatestEvent { + room_id, + timestamp: event_tl_item.timestamp(), + latest_message_text, + }); + room_avatar_changed +} + /// Fetches and returns the avatar image for the given room (if one exists), /// otherwise returns a text avatar string of the first character of the room name. async fn room_avatar(room: &Room, room_name: &Option) -> RoomPreviewAvatar { From 6487e4fba5d2c07a1e60d6875682e9ec2b204910 Mon Sep 17 00:00:00 2001 From: Kevin Boos Date: Mon, 30 Sep 2024 13:06:18 -0700 Subject: [PATCH 4/6] Simplified native sliding sync works. Still need to properly handle latest events and room avatars. --- src/home/room_preview.rs | 2 +- src/home/rooms_list.rs | 36 ++++----- src/persistent_state.rs | 7 +- src/sliding_sync.rs | 155 +++++++++++++++++---------------------- 4 files changed, 86 insertions(+), 114 deletions(-) diff --git a/src/home/room_preview.rs b/src/home/room_preview.rs index 730cfa43..cf87a552 100644 --- a/src/home/room_preview.rs +++ b/src/home/room_preview.rs @@ -40,7 +40,7 @@ live_design! { font_size: 7.5 }, } - text: "[Timestamp unknown]" + text: "??" } MessagePreview = { diff --git a/src/home/rooms_list.rs b/src/home/rooms_list.rs index a4f31197..16d777c6 100644 --- a/src/home/rooms_list.rs +++ b/src/home/rooms_list.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use crossbeam_queue::SegQueue; use makepad_widgets::*; -use matrix_sdk::{ruma::{MilliSecondsSinceUnixEpoch, OwnedRoomId}, Room}; +use matrix_sdk::ruma::{MilliSecondsSinceUnixEpoch, OwnedRoomId}; use super::room_preview::RoomPreviewAction; @@ -35,7 +35,7 @@ live_design! { color: (MESSAGE_TEXT_COLOR), text_style: {} } - text: "Loading joined rooms..." + text: "Loading rooms..." } } @@ -70,7 +70,7 @@ pub enum RoomsListUpdate { NotLoaded, /// Some rooms were loaded, and the server optionally told us /// the max number of rooms that will ever be loaded. - LoadedRooms{ max_rooms: Option }, + LoadedRooms{ max_rooms: Option }, /// Add a new room to the list of all rooms. AddRoom(RoomPreviewEntry), /// Clear all rooms in the list of all rooms. @@ -123,7 +123,7 @@ pub enum RoomListAction { None, } -#[derive(Debug, Default)] +#[derive(Debug)] pub struct RoomPreviewEntry { /// The matrix ID of this room. pub room_id: OwnedRoomId, @@ -165,7 +165,7 @@ pub struct RoomsList { /// The index of the currently selected room #[rust] current_active_room_index: Option, /// The maximum number of rooms that will ever be loaded. - #[rust] max_known_rooms: Option, + #[rust] max_known_rooms: Option, } impl RoomsList { @@ -190,28 +190,28 @@ impl Widget for RoomsList { self.all_rooms.push(room); } RoomsListUpdate::UpdateRoomAvatar { room_id, avatar } => { - if let Some(room) = self.all_rooms.iter_mut().find(|r| r.room_id.as_ref() == Some(&room_id)) { + if let Some(room) = self.all_rooms.iter_mut().find(|r| &r.room_id == &room_id) { room.avatar = avatar; } else { error!("Error: couldn't find room {room_id} to update avatar"); } } RoomsListUpdate::UpdateLatestEvent { room_id, timestamp, latest_message_text } => { - if let Some(room) = self.all_rooms.iter_mut().find(|r| r.room_id.as_ref() == Some(&room_id)) { + if let Some(room) = self.all_rooms.iter_mut().find(|r| &r.room_id == &room_id) { room.latest = Some((timestamp, latest_message_text)); } else { error!("Error: couldn't find room {room_id} to update latest event"); } } RoomsListUpdate::UpdateRoomName { room_id, new_room_name } => { - if let Some(room) = self.all_rooms.iter_mut().find(|r| r.room_id.as_ref() == Some(&room_id)) { + if let Some(room) = self.all_rooms.iter_mut().find(|r| &r.room_id == &room_id) { room.room_name = Some(new_room_name); } else { error!("Error: couldn't find room {room_id} to update room name"); } } RoomsListUpdate::RemoveRoom(room_id) => { - if let Some(idx) = self.all_rooms.iter().position(|r| r.room_id.as_ref() == Some(&room_id)) { + if let Some(idx) = self.all_rooms.iter().position(|r| &r.room_id == &room_id) { self.all_rooms.remove(idx); } else { error!("Error: couldn't find room {room_id} to remove room"); @@ -256,7 +256,7 @@ impl Widget for RoomsList { &scope.path, RoomListAction::Selected { room_index, - room_id: room_details.room_id.clone().unwrap(), + room_id: room_details.room_id.to_owned(), room_name: room_details.room_name.clone(), } ); @@ -288,18 +288,10 @@ impl Widget for RoomsList { // Draw the status label as the bottom entry. let item = if item_id == last_item_id { let item = list.item(cx, item_id, live_id!(status_label)).unwrap(); - if count > 0 { - let text = format!("Found {count} joined rooms."); - item.as_view().apply_over(cx, live!{ - height: 80.0, - label = { text: (text) } - }); - } else { - item.as_view().apply_over(cx, live!{ - height: Fit, - label = { text: (&self.status) } - }); - } + item.as_view().apply_over(cx, live!{ + height: Fit, + label = { text: (&self.status) } + }); item } // Draw a filler entry to take up space at the bottom of the portal list. diff --git a/src/persistent_state.rs b/src/persistent_state.rs index 75d996e9..b38b13e6 100644 --- a/src/persistent_state.rs +++ b/src/persistent_state.rs @@ -4,9 +4,7 @@ use std::path::PathBuf; use anyhow::{anyhow, bail}; use makepad_widgets::log; use matrix_sdk::{ - matrix_auth::MatrixSession, - ruma::{OwnedUserId, UserId}, - Client, + matrix_auth::MatrixSession, ruma::{OwnedUserId, UserId}, sliding_sync::VersionBuilder, Client }; use serde::{Deserialize, Serialize}; use tokio::fs; @@ -110,7 +108,8 @@ pub async fn restore_session( let client = Client::builder() .server_name_or_homeserver_url(client_session.homeserver) .sqlite_store(client_session.db_path, Some(&client_session.passphrase)) - .simplified_sliding_sync(false) + .sliding_sync_version_builder(VersionBuilder::DiscoverNative) + .handle_refresh_tokens() .build() .await?; diff --git a/src/sliding_sync.rs b/src/sliding_sync.rs index f8680f58..78be99e0 100644 --- a/src/sliding_sync.rs +++ b/src/sliding_sync.rs @@ -2,21 +2,20 @@ use anyhow::{Result, bail}; use clap::Parser; use eyeball::Subscriber; use eyeball_im::VectorDiff; -use futures_util::{pin_mut, StreamExt}; -use imbl::Vector; +use futures_util::StreamExt; use makepad_widgets::{error, log, warning, SignalToUI}; use matrix_sdk::{ config::RequestConfig, event_handler::EventHandlerDropGuard, media::MediaRequest, room::RoomMember, ruma::{ - api::client::session::get_login_types::v3::LoginType, events::{room::{message::{ForwardThread, RoomMessageEventContent}, MediaSource}, FullStateEventContent, StateEventType}, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedMxcUri, OwnedRoomAliasId, OwnedRoomId, OwnedUserId, UInt, UserId - }, sliding_sync::VersionBuilder, Client, Room, SlidingSyncList, SlidingSyncMode + api::client::session::get_login_types::v3::LoginType, events::{room::{message::{ForwardThread, RoomMessageEventContent}, MediaSource}, FullStateEventContent}, OwnedEventId, OwnedMxcUri, OwnedRoomAliasId, OwnedRoomId, OwnedUserId, RoomId, UserId + }, sliding_sync::VersionBuilder, Client, Room }; -use matrix_sdk_ui::{room_list_service::{self, RoomListLoadingState}, sync_service::{self, SyncService}, timeline::{AnyOtherFullStateEventContent, EventTimelineItem, LiveBackPaginationStatus, RepliedToInfo, TimelineDetails, TimelineItem, TimelineItemContent}, RoomListService, Timeline}; +use matrix_sdk_ui::{room_list_service::{self, RoomListLoadingState}, sync_service::{self, SyncService}, timeline::{AnyOtherFullStateEventContent, EventTimelineItem, LiveBackPaginationStatus, RepliedToInfo, TimelineDetails, TimelineItemContent}, Timeline}; use tokio::{ runtime::Handle, sync::mpsc::{UnboundedSender, UnboundedReceiver}, task::JoinHandle, }; use unicode_segmentation::UnicodeSegmentation; -use std::{cmp::{max, min}, collections::{BTreeMap, BTreeSet}, ops::Range, path:: Path, sync::{Arc, Mutex, OnceLock}}; +use std::{cmp::{max, min}, collections::{BTreeMap, BTreeSet}, path:: Path, sync::{Arc, Mutex, OnceLock}}; use crate::{ app_data_dir, avatar_cache::AvatarUpdate, event_preview::text_preview_of_timeline_item, home::{ @@ -82,7 +81,8 @@ async fn build_client( // Use a sqlite database to persist the client's encryption setup. .sqlite_store(&db_path, Some(&passphrase)) // The sliding sync proxy has now been deprecated in favor of native sliding sync. - .sliding_sync_version_builder(VersionBuilder::DiscoverNative); + .sliding_sync_version_builder(VersionBuilder::DiscoverNative) + .handle_refresh_tokens(); if let Some(proxy) = cli.proxy.as_ref() { builder = builder.proxy(proxy.clone()); @@ -95,7 +95,6 @@ async fn build_client( .timeout(std::time::Duration::from_secs(60)) ); - builder = builder.handle_refresh_tokens(); let client = builder.build().await?; Ok(( client, @@ -689,8 +688,6 @@ pub fn start_matrix_tokio() -> Result<()> { struct RoomInfo { #[allow(unused)] room_id: OwnedRoomId, - /// The timestamp of the latest event that we've seen for this room. - latest_event_timestamp: MilliSecondsSinceUnixEpoch, /// A reference to this room's timeline of events. timeline: Arc, /// An instance of the clone-able sender that can be used to send updates to this room's timeline. @@ -723,7 +720,7 @@ pub fn get_client() -> Option { /// The singleton sync service. static SYNC_SERVICE: OnceLock = OnceLock::new(); -pub fn get_sync_service() -> Option<&SyncService> { +pub fn get_sync_service() -> Option<&'static SyncService> { SYNC_SERVICE.get() } @@ -766,8 +763,6 @@ pub fn take_timeline_update_receiver( async fn async_main_loop() -> Result<()> { tracing_subscriber::fmt::init(); - let start = std::time::Instant::now(); - let cli = Cli::try_parse().ok().or_else(|| { // Quickly try to parse the username, password, and homeserver fields from "login.toml". let login_file = std::include_str!("../login.toml"); @@ -861,15 +856,14 @@ async fn async_main_loop() -> Result<()> { sync_service.start().await; handle_sync_service_state_subscriber(sync_service.state()); let room_list_service = sync_service.room_list_service(); - SYNC_SERVICE.set(sync_service).expect("BUG: SYNC_SERVICE already set!"); + SYNC_SERVICE.set(sync_service).unwrap_or_else(|_| panic!("BUG: SYNC_SERVICE already set!")); let all_rooms_list = room_list_service.all_rooms().await?; - handle_room_list_service(all_rooms_list.loading_state()); - let (known_rooms, room_diff_stream) = all_rooms_list.entries(); - for room in known_rooms.iter() { - add_new_room(room); + handle_room_list_service_loading_state(all_rooms_list.loading_state()); + let (mut all_known_rooms, mut room_diff_stream) = all_rooms_list.entries(); + for room in all_known_rooms.iter() { + add_new_room(room).await?; } - let mut all_known_rooms = known_rooms; const LOG_ROOM_LIST_DIFFS: bool = true; @@ -919,7 +913,6 @@ async fn async_main_loop() -> Result<()> { if LOG_ROOM_LIST_DIFFS { log!("room_list: diff Insert at {index}"); } add_new_room(&new_room).await?; all_known_rooms.insert(index, new_room); - num_updates += 1; } VectorDiff::Set { index, value: changed_room } => { if LOG_ROOM_LIST_DIFFS { log!("room_list: diff Set at {index} !!!!!!!!"); } @@ -928,8 +921,11 @@ async fn async_main_loop() -> Result<()> { } VectorDiff::Remove { index } => { if LOG_ROOM_LIST_DIFFS { log!("room_list: diff Remove at {index}"); } - if let Some(room) = all_known_rooms.remove(index) { + if index < all_known_rooms.len() { + let room = all_known_rooms.remove(index); remove_room(room); + } else { + error!("BUG: room_list: diff Remove index {index} out of bounds, len {}", all_known_rooms.len()); } } VectorDiff::Truncate { length } => { @@ -943,7 +939,7 @@ async fn async_main_loop() -> Result<()> { } VectorDiff::Reset { values } => { // We implement this by clearing all rooms and then adding back the new values. - if LOG_ROOM_LIST_DIFFS { log!("room_list: diff Reset, new length {values.len()}"); } + if LOG_ROOM_LIST_DIFFS { log!("room_list: diff Reset, new length {}", values.len()); } all_known_rooms = values; ALL_ROOM_INFO.lock().unwrap().clear(); // TODO: we probably need to remove each room individually to kill off @@ -963,7 +959,7 @@ async fn async_main_loop() -> Result<()> { /// Invoked when the room list service has received an update that changes an existing room. -async fn update_room(room: &room_list_service::Room) -> matrix_sdk::Result<()> { +async fn update_room(_room: &room_list_service::Room) -> matrix_sdk::Result<()> { todo!("update_room") } @@ -981,8 +977,7 @@ fn remove_room(room: room_list_service::Room) { /// Invoked when the room list service has received an update with a brand new room. -async fn add_new_room(room: &room_list_service::Room) -> matrix_sdk::Result<()> { - let room_avatar_changed = true; +async fn add_new_room(room: &room_list_service::Room) -> Result<()> { let room_id = room.room_id().to_owned(); let timeline = room.default_room_timeline_builder().await? @@ -1032,16 +1027,13 @@ async fn add_new_room(room: &room_list_service::Room) -> matrix_sdk::Result<()> // start with a basic text avatar; the avatar image will be fetched asynchronously later. avatar: avatar_from_room_name(room_name.as_deref().unwrap_or_default()), room_name, - ..RoomPreviewEntry::default() + is_selected: false, })); ALL_ROOM_INFO.lock().unwrap().insert( room_id.clone(), RoomInfo { room_id, - latest_event_timestamp: latest_event.as_ref() - .map(|ev| ev.timestamp()) - .unwrap_or(MilliSecondsSinceUnixEpoch(UInt::MIN)), timeline: tl_arc, timeline_update_receiver: Some(timeline_update_receiver), timeline_update_sender, @@ -1049,6 +1041,8 @@ async fn add_new_room(room: &room_list_service::Room) -> matrix_sdk::Result<()> typing_notice_subscriber: None, }, ); + + Ok(()) } #[allow(unused)] @@ -1100,9 +1094,9 @@ fn handle_ignore_user_list_subscriber(client: Client) { } -fn handle_sync_service_state_subscriber(subscriber: Subscriber) { +fn handle_sync_service_state_subscriber(mut subscriber: Subscriber) { Handle::current().spawn(async move { - while let Some(state) = subsriber.next().await { + while let Some(state) = subscriber.next().await { log!("Received a sync service state update: {state:?}"); if state == sync_service::State::Error { log!("Restarting sync service due to error."); @@ -1115,10 +1109,10 @@ fn handle_sync_service_state_subscriber(subscriber: Subscriber) { +fn handle_room_list_service_loading_state(mut loading_state: Subscriber) { Handle::current().spawn(async move { - while let Some(loading_state) = loading_state.next().await { - match loading_state { + while let Some(state) = loading_state.next().await { + match state { RoomListLoadingState::NotLoaded => { enqueue_rooms_list_update(RoomsListUpdate::NotLoaded); } @@ -1130,13 +1124,14 @@ fn handle_room_list_service_loading_state(loading_state: Subscriber, sender: crossbeam_channel::Sender, ) { - let room_id = room.room_id(); + let room_id = room.room_id().to_owned(); log!("Starting timeline subscriber for room {room_id}..."); let (mut timeline_items, mut subscriber) = timeline.subscribe_batched().await; log!("Received initial timeline update for room {room_id}."); @@ -1147,50 +1142,8 @@ async fn timeline_subscriber_handler( clear_cache: true, }).expect("Error: timeline update sender couldn't send update with initial items!"); - const LOG_TIMELINE_DIFFS: bool = false; - let mut latest_event = timeline.latest_event().await; - let room_id2 = room_id.clone(); - let send_update = | - timeline_items: Vector>, - changed_indices: Range, - clear_cache: bool, - num_updates: usize, - new_latest_event: Option, - | { - if LOG_TIMELINE_DIFFS { - log!("timeline_subscriber: applied {num_updates} updates for room {room_id}, timeline now has {} items. Clear cache? {clear_cache}. Changes: {changed_indices:?}.", timeline_items.len()); - } - sender.send(TimelineUpdate::NewItems { - items: timeline_items, - changed_indices, - clear_cache, - }).expect("Error: timeline update sender couldn't send update with new items!"); - - // Send a Makepad-level signal to update this room's timeline UI view. - SignalToUI::set_ui_signal(); - - // Update the latest event for this room. - if let Some(new_latest) = new_latest_event { - if latest_event.is_some_and(|ev| ev.timestamp() < new_latest.timestamp()) { - let room_avatar_changed = update_latest_event(room_id2, new_latest); - latest_event = Some(new_latest_event); - if room_avatar_changed { - // Spawn a new async task to fetch the room's new avatar. - let room_name_str = room.cached_display_name(); - Handle::current().spawn(async move { - let avatar = room_avatar(&inner_room, &room_name_str).await; - rooms_list::enqueue_rooms_list_update(RoomsListUpdate::UpdateRoomAvatar { - room_id, - avatar, - }); - }); - } - } - } - }; - while let Some(batch) = subscriber.next().await { let mut num_updates = 0; let mut reobtain_latest_event = false; @@ -1305,13 +1258,41 @@ async fn timeline_subscriber_handler( } else { None }; - send_update( - timeline_items.clone(), - index_of_first_change..index_of_last_change, + + let changed_indices = index_of_first_change..index_of_last_change; + + if LOG_TIMELINE_DIFFS { + log!("timeline_subscriber: applied {num_updates} updates for room {room_id}, timeline now has {} items. Clear cache? {clear_cache}. Changes: {changed_indices:?}.", timeline_items.len()); + } + sender.send(TimelineUpdate::NewItems { + items: timeline_items.clone(), + changed_indices, clear_cache, - num_updates, - new_latest_event, - ); + }).expect("Error: timeline update sender couldn't send update with new items!"); + + // Send a Makepad-level signal to update this room's timeline UI view. + SignalToUI::set_ui_signal(); + + // Update the latest event for this room. + if let Some(new_latest) = new_latest_event { + if latest_event.as_ref().is_some_and(|ev| ev.timestamp() < new_latest.timestamp()) { + let room_avatar_changed = update_latest_event(&room_id, &new_latest); + latest_event = Some(new_latest); + if room_avatar_changed { + // Spawn a new async task to fetch the room's new avatar. + let room_name_str = room.cached_display_name().map(|dn| dn.to_string()); + let room2 = room.clone(); + let room_id2 = room_id.to_owned(); + Handle::current().spawn(async move { + let avatar = room_avatar(&room2, &room_name_str).await; + rooms_list::enqueue_rooms_list_update(RoomsListUpdate::UpdateRoomAvatar { + room_id: room_id2, + avatar, + }); + }); + } + } + } } } @@ -1324,8 +1305,8 @@ async fn timeline_subscriber_handler( /// Returns `true` if this latest event indicates that the room's avatar has changed /// and should also be updated. fn update_latest_event( - room_id: OwnedRoomId, - event_tl_item: EventTimelineItem, + room_id: &RoomId, + event_tl_item: &EventTimelineItem, ) -> bool { let mut room_avatar_changed = false; @@ -1354,7 +1335,7 @@ fn update_latest_event( TimelineItemContent::OtherState(other) => match other.content() { AnyOtherFullStateEventContent::RoomName(FullStateEventContent::Original { content, .. }) => { rooms_list::enqueue_rooms_list_update(RoomsListUpdate::UpdateRoomName { - room_id: room_id.clone(), + room_id: room_id.to_owned(), new_room_name: content.name.clone(), }); } @@ -1366,7 +1347,7 @@ fn update_latest_event( _ => { } } enqueue_rooms_list_update(RoomsListUpdate::UpdateLatestEvent { - room_id, + room_id: room_id.to_owned(), timestamp: event_tl_item.timestamp(), latest_message_text, }); From facdaf53257c95e3f07b4d5de7b15e311f7b6360 Mon Sep 17 00:00:00 2001 From: Kevin Boos Date: Mon, 30 Sep 2024 15:32:47 -0700 Subject: [PATCH 5/6] Fix latest message previews, room avatars, room preview font color --- src/home/room_preview.rs | 4 ++- src/home/room_screen.rs | 2 ++ src/shared/html_or_plaintext.rs | 2 +- src/sliding_sync.rs | 45 ++++++++++++++++++++------------- src/utils.rs | 4 +-- 5 files changed, 36 insertions(+), 21 deletions(-) diff --git a/src/home/room_preview.rs b/src/home/room_preview.rs index cf87a552..b8212b41 100644 --- a/src/home/room_preview.rs +++ b/src/home/room_preview.rs @@ -48,6 +48,7 @@ live_design! { flow: Down, spacing: 5. latest_message = { + padding: {top: 3.0} html_view = { html = { font_size: 9.3, line_spacing: 1., draw_normal: { text_style: { font_size: 9.3, line_spacing: 1. } }, @@ -60,7 +61,7 @@ live_design! { draw_text: { text_style: { font_size: 9.5, line_spacing: 1. }, } - text: "[Latest message unknown]" + text: "[Loading latest message]" } } } } @@ -301,6 +302,7 @@ impl RoomPreviewContent { live!( html_view = { html = { + font_color: (message_text_color), draw_normal: { color: (message_text_color) }, draw_italic: { color: (message_text_color) }, draw_bold: { color: (message_text_color) }, diff --git a/src/home/room_screen.rs b/src/home/room_screen.rs index 43aee6cc..91165342 100644 --- a/src/home/room_screen.rs +++ b/src/home/room_screen.rs @@ -276,12 +276,14 @@ live_design! { visible: false, width: Fill, height: Fit, + padding: {top: 5.0} html_content = { width: Fill, height: Fit, padding: { bottom: 5.0, top: 0.0 }, font_size: 10.5, + font_color: (REACTION_TEXT_COLOR), draw_normal: { color: (REACTION_TEXT_COLOR) }, draw_italic: { color: (REACTION_TEXT_COLOR) }, draw_bold: { color: (REACTION_TEXT_COLOR) }, diff --git a/src/shared/html_or_plaintext.rs b/src/shared/html_or_plaintext.rs index dc2bcc86..03b44e17 100644 --- a/src/shared/html_or_plaintext.rs +++ b/src/shared/html_or_plaintext.rs @@ -94,7 +94,7 @@ live_design! { draw_text: { wrap: Word, color: (MESSAGE_TEXT_COLOR), - text_style: { }, + text_style: { font_size: (MESSAGE_FONT_SIZE) }, } text: "[plaintext message placeholder]", } diff --git a/src/sliding_sync.rs b/src/sliding_sync.rs index 78be99e0..6c84fd21 100644 --- a/src/sliding_sync.rs +++ b/src/sliding_sync.rs @@ -836,7 +836,7 @@ async fn async_main_loop() -> Result<()> { }; user_id_str.as_str().try_into().ok() }); - let (client, sync_token) = if let Ok(restored) = persistent_state::restore_session(specified_username).await { + let (client, _sync_token) = if let Ok(restored) = persistent_state::restore_session(specified_username).await { restored } else { match login(cli).await { @@ -853,14 +853,15 @@ async fn async_main_loop() -> Result<()> { let sync_service = SyncService::builder(client.clone()) .build() .await?; - sync_service.start().await; handle_sync_service_state_subscriber(sync_service.state()); + sync_service.start().await; let room_list_service = sync_service.room_list_service(); SYNC_SERVICE.set(sync_service).unwrap_or_else(|_| panic!("BUG: SYNC_SERVICE already set!")); let all_rooms_list = room_list_service.all_rooms().await?; handle_room_list_service_loading_state(all_rooms_list.loading_state()); let (mut all_known_rooms, mut room_diff_stream) = all_rooms_list.entries(); + log!("Populating initial set of {} known rooms.", all_known_rooms.len()); for room in all_known_rooms.iter() { add_new_room(room).await?; } @@ -980,6 +981,8 @@ fn remove_room(room: room_list_service::Room) { async fn add_new_room(room: &room_list_service::Room) -> Result<()> { let room_id = room.room_id().to_owned(); + log!("Adding new room: {:?}, room_id: {room_id}", room.compute_display_name().await.map(|n| n.to_string()).unwrap_or_default()); + let timeline = room.default_room_timeline_builder().await? .track_read_marker_and_receipts() .build() @@ -1024,12 +1027,14 @@ async fn add_new_room(room: &room_list_service::Room) -> Result<()> { rooms_list::enqueue_rooms_list_update(RoomsListUpdate::AddRoom(RoomPreviewEntry { room_id: room_id.clone(), latest, - // start with a basic text avatar; the avatar image will be fetched asynchronously later. + // start with a basic text avatar; the avatar image will be fetched asynchronously below. avatar: avatar_from_room_name(room_name.as_deref().unwrap_or_default()), room_name, is_selected: false, })); + spawn_fetch_room_avatar(room.inner_room().clone()); + ALL_ROOM_INFO.lock().unwrap().insert( room_id.clone(), RoomInfo { @@ -1112,6 +1117,7 @@ fn handle_sync_service_state_subscriber(mut subscriber: Subscriber) { Handle::current().spawn(async move { while let Some(state) = loading_state.next().await { + log!("Received a room list loading state update: {state:?}"); match state { RoomListLoadingState::NotLoaded => { enqueue_rooms_list_update(RoomsListUpdate::NotLoaded); @@ -1134,7 +1140,7 @@ async fn timeline_subscriber_handler( let room_id = room.room_id().to_owned(); log!("Starting timeline subscriber for room {room_id}..."); let (mut timeline_items, mut subscriber) = timeline.subscribe_batched().await; - log!("Received initial timeline update for room {room_id}."); + log!("Received initial timeline update of {} items for room {room_id}.", timeline_items.len()); sender.send(TimelineUpdate::NewItems { items: timeline_items.clone(), @@ -1146,7 +1152,8 @@ async fn timeline_subscriber_handler( while let Some(batch) = subscriber.next().await { let mut num_updates = 0; - let mut reobtain_latest_event = false; + // For now we always requery the latest event, but this can be better optimized. + let mut reobtain_latest_event = true; let mut index_of_first_change = usize::MAX; let mut index_of_last_change = usize::MIN; let mut clear_cache = false; // whether to clear the entire cache of items @@ -1275,21 +1282,11 @@ async fn timeline_subscriber_handler( // Update the latest event for this room. if let Some(new_latest) = new_latest_event { - if latest_event.as_ref().is_some_and(|ev| ev.timestamp() < new_latest.timestamp()) { + if latest_event.as_ref().map_or(true, |ev| ev.timestamp() < new_latest.timestamp()) { let room_avatar_changed = update_latest_event(&room_id, &new_latest); latest_event = Some(new_latest); if room_avatar_changed { - // Spawn a new async task to fetch the room's new avatar. - let room_name_str = room.cached_display_name().map(|dn| dn.to_string()); - let room2 = room.clone(); - let room_id2 = room_id.to_owned(); - Handle::current().spawn(async move { - let avatar = room_avatar(&room2, &room_name_str).await; - rooms_list::enqueue_rooms_list_update(RoomsListUpdate::UpdateRoomAvatar { - room_id: room_id2, - avatar, - }); - }); + spawn_fetch_room_avatar(room.clone()); } } } @@ -1354,6 +1351,20 @@ fn update_latest_event( room_avatar_changed } + +/// Spawn a new async task to fetch the room's new avatar. +fn spawn_fetch_room_avatar(room: Room) { + let room_id = room.room_id().to_owned(); + let room_name_str = room.cached_display_name().map(|dn| dn.to_string()); + Handle::current().spawn(async move { + let avatar = room_avatar(&room, &room_name_str).await; + rooms_list::enqueue_rooms_list_update(RoomsListUpdate::UpdateRoomAvatar { + room_id, + avatar, + }); + }); +} + /// Fetches and returns the avatar image for the given room (if one exists), /// otherwise returns a text avatar string of the first character of the room name. async fn room_avatar(room: &Room, room_name: &Option) -> RoomPreviewAvatar { diff --git a/src/utils.rs b/src/utils.rs index d5530d3f..fdebe1e3 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -93,9 +93,9 @@ pub fn relative_format(millis: &MilliSecondsSinceUnixEpoch) -> Option { // Handle different time ranges and format accordingly if duration < Duration::seconds(60) { - Some("Just now".to_string()) + Some("Now".to_string()) } else if duration < Duration::minutes(60) { - let minutes_text = if duration.num_minutes() == 1 { "minute" } else { "minutes" }; + let minutes_text = if duration.num_minutes() == 1 { "min" } else { "mins" }; Some(format!("{} {} ago", duration.num_minutes(), minutes_text)) } else if duration < Duration::hours(24) && now.date_naive() == datetime.date_naive() { Some(format!("{}", datetime.format("%H:%M"))) // "HH:MM" format for today From 5212ae5ad5e77d25d5e4475ed2e849f7efcb2ebc Mon Sep 17 00:00:00 2001 From: Kevin Boos Date: Mon, 30 Sep 2024 17:42:00 -0700 Subject: [PATCH 6/6] Update matrix-sdk version. Don't re-paginate upon the very first ignored-user list update. --- Cargo.lock | 16 +++++++-------- src/home/room_screen.rs | 4 +++- src/sliding_sync.rs | 44 +++++++++++++++++++++-------------------- 3 files changed, 34 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ff7f071e..7459b559 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2033,7 +2033,7 @@ dependencies = [ [[package]] name = "matrix-sdk" version = "0.7.1" -source = "git+https://github.com/matrix-org/matrix-rust-sdk#5ba90611b43915926a37be83f3de1d4d53bca6bd" +source = "git+https://github.com/matrix-org/matrix-rust-sdk#dc055c632c106c6392eeea8efd24c3bb7d756fee" dependencies = [ "anymap2", "aquamarine", @@ -2080,7 +2080,7 @@ dependencies = [ [[package]] name = "matrix-sdk-base" version = "0.7.0" -source = "git+https://github.com/matrix-org/matrix-rust-sdk#5ba90611b43915926a37be83f3de1d4d53bca6bd" +source = "git+https://github.com/matrix-org/matrix-rust-sdk#dc055c632c106c6392eeea8efd24c3bb7d756fee" dependencies = [ "as_variant", "async-trait", @@ -2104,7 +2104,7 @@ dependencies = [ [[package]] name = "matrix-sdk-common" version = "0.7.0" -source = "git+https://github.com/matrix-org/matrix-rust-sdk#5ba90611b43915926a37be83f3de1d4d53bca6bd" +source = "git+https://github.com/matrix-org/matrix-rust-sdk#dc055c632c106c6392eeea8efd24c3bb7d756fee" dependencies = [ "async-trait", "futures-core", @@ -2125,7 +2125,7 @@ dependencies = [ [[package]] name = "matrix-sdk-crypto" version = "0.7.2" -source = "git+https://github.com/matrix-org/matrix-rust-sdk#5ba90611b43915926a37be83f3de1d4d53bca6bd" +source = "git+https://github.com/matrix-org/matrix-rust-sdk#dc055c632c106c6392eeea8efd24c3bb7d756fee" dependencies = [ "aes", "as_variant", @@ -2165,7 +2165,7 @@ dependencies = [ [[package]] name = "matrix-sdk-indexeddb" version = "0.7.0" -source = "git+https://github.com/matrix-org/matrix-rust-sdk#5ba90611b43915926a37be83f3de1d4d53bca6bd" +source = "git+https://github.com/matrix-org/matrix-rust-sdk#dc055c632c106c6392eeea8efd24c3bb7d756fee" dependencies = [ "anyhow", "async-trait", @@ -2193,7 +2193,7 @@ dependencies = [ [[package]] name = "matrix-sdk-sqlite" version = "0.7.1" -source = "git+https://github.com/matrix-org/matrix-rust-sdk#5ba90611b43915926a37be83f3de1d4d53bca6bd" +source = "git+https://github.com/matrix-org/matrix-rust-sdk#dc055c632c106c6392eeea8efd24c3bb7d756fee" dependencies = [ "async-trait", "deadpool-sqlite", @@ -2215,7 +2215,7 @@ dependencies = [ [[package]] name = "matrix-sdk-store-encryption" version = "0.7.0" -source = "git+https://github.com/matrix-org/matrix-rust-sdk#5ba90611b43915926a37be83f3de1d4d53bca6bd" +source = "git+https://github.com/matrix-org/matrix-rust-sdk#dc055c632c106c6392eeea8efd24c3bb7d756fee" dependencies = [ "base64", "blake3", @@ -2234,7 +2234,7 @@ dependencies = [ [[package]] name = "matrix-sdk-ui" version = "0.7.0" -source = "git+https://github.com/matrix-org/matrix-rust-sdk#5ba90611b43915926a37be83f3de1d4d53bca6bd" +source = "git+https://github.com/matrix-org/matrix-rust-sdk#dc055c632c106c6392eeea8efd24c3bb7d756fee" dependencies = [ "as_variant", "async-once-cell", diff --git a/src/home/room_screen.rs b/src/home/room_screen.rs index 91165342..f8e94273 100644 --- a/src/home/room_screen.rs +++ b/src/home/room_screen.rs @@ -953,7 +953,9 @@ impl Widget for RoomScreen { match update { TimelineUpdate::NewItems { items, changed_indices, clear_cache } => { if items.is_empty() { - log!("Timeline::handle_event(): timeline was cleared for room {}", tl.room_id); + if !tl.items.is_empty() { + log!("Timeline::handle_event(): timeline was cleared for room {}", tl.room_id); + } // If the bottom of the timeline (the last event) is visible, then we should // set the timeline to live mode. diff --git a/src/sliding_sync.rs b/src/sliding_sync.rs index 6c84fd21..6065e346 100644 --- a/src/sliding_sync.rs +++ b/src/sliding_sync.rs @@ -983,15 +983,14 @@ async fn add_new_room(room: &room_list_service::Room) -> Result<()> { log!("Adding new room: {:?}, room_id: {room_id}", room.compute_display_name().await.map(|n| n.to_string()).unwrap_or_default()); - let timeline = room.default_room_timeline_builder().await? - .track_read_marker_and_receipts() - .build() - .await?; - + let timeline = { + let builder = room.default_room_timeline_builder().await? + .track_read_marker_and_receipts(); + room.init_timeline_with_builder(builder).await?; + room.timeline().ok_or_else(|| anyhow::anyhow!("BUG: room timeline not found for room {room_id}"))? + }; let latest_event = timeline.latest_event().await; - let (timeline_update_sender, timeline_update_receiver) = crossbeam_channel::unbounded(); - let tl_arc = Arc::new(timeline); let room_name = room.compute_display_name().await .map(|n| n.to_string()) @@ -999,7 +998,7 @@ async fn add_new_room(room: &room_list_service::Room) -> Result<()> { Handle::current().spawn(timeline_subscriber_handler( room.inner_room().clone(), - tl_arc.clone(), + timeline.clone(), timeline_update_sender.clone(), )); @@ -1039,7 +1038,7 @@ async fn add_new_room(room: &room_list_service::Room) -> Result<()> { room_id.clone(), RoomInfo { room_id, - timeline: tl_arc, + timeline, timeline_update_receiver: Some(timeline_update_receiver), timeline_update_sender, pagination_status_task: None, @@ -1069,6 +1068,7 @@ async fn current_ignore_user_list(client: &Client) -> Option { - if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: diff Clear"); } + if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: room {room_id} diff Clear"); } clear_cache = true; timeline_items.clear(); reobtain_latest_event = true; num_updates += 1; } VectorDiff::PushFront { value } => { - if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: diff PushFront"); } + if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: room {room_id} diff PushFront"); } clear_cache = true; timeline_items.push_front(value); reobtain_latest_event |= latest_event.is_none(); @@ -1186,12 +1188,12 @@ async fn timeline_subscriber_handler( index_of_first_change = min(index_of_first_change, timeline_items.len()); timeline_items.push_back(value); index_of_last_change = max(index_of_last_change, timeline_items.len()); - if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: diff PushBack. Changes: {index_of_first_change}..{index_of_last_change}"); } + if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: room {room_id} diff PushBack. Changes: {index_of_first_change}..{index_of_last_change}"); } reobtain_latest_event = true; num_updates += 1; } VectorDiff::PopFront => { - if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: diff PopFront"); } + if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: room {room_id} diff PopFront"); } clear_cache = true; timeline_items.pop_front(); // This doesn't affect whether we should reobtain the latest event. @@ -1201,7 +1203,7 @@ async fn timeline_subscriber_handler( timeline_items.pop_back(); index_of_first_change = min(index_of_first_change, timeline_items.len()); index_of_last_change = usize::MAX; - if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: diff PopBack. Changes: {index_of_first_change}..{index_of_last_change}"); } + if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: room {room_id} diff PopBack. Changes: {index_of_first_change}..{index_of_last_change}"); } reobtain_latest_event = true; num_updates += 1; } @@ -1213,7 +1215,7 @@ async fn timeline_subscriber_handler( index_of_last_change = usize::MAX; } timeline_items.insert(index, value); - if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: diff Insert at {index}. Changes: {index_of_first_change}..{index_of_last_change}"); } + if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: room {room_id} diff Insert at {index}. Changes: {index_of_first_change}..{index_of_last_change}"); } reobtain_latest_event = true; num_updates += 1; } @@ -1221,7 +1223,7 @@ async fn timeline_subscriber_handler( index_of_first_change = min(index_of_first_change, index); index_of_last_change = max(index_of_last_change, index.saturating_add(1)); timeline_items.set(index, value); - if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: diff Set at {index}. Changes: {index_of_first_change}..{index_of_last_change}"); } + if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: room {room_id} diff Set at {index}. Changes: {index_of_first_change}..{index_of_last_change}"); } reobtain_latest_event = true; num_updates += 1; } @@ -1233,7 +1235,7 @@ async fn timeline_subscriber_handler( index_of_last_change = usize::MAX; } timeline_items.remove(index); - if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: diff Remove at {index}. Changes: {index_of_first_change}..{index_of_last_change}"); } + if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: room {room_id} diff Remove at {index}. Changes: {index_of_first_change}..{index_of_last_change}"); } reobtain_latest_event = true; num_updates += 1; } @@ -1245,12 +1247,12 @@ async fn timeline_subscriber_handler( index_of_last_change = usize::MAX; } timeline_items.truncate(length); - if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: diff Truncate to length {length}. Changes: {index_of_first_change}..{index_of_last_change}"); } + if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: room {room_id} diff Truncate to length {length}. Changes: {index_of_first_change}..{index_of_last_change}"); } reobtain_latest_event = true; num_updates += 1; } VectorDiff::Reset { values } => { - if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: diff Reset, new length {}", values.len()); } + if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: room {room_id} diff Reset, new length {}", values.len()); } clear_cache = true; // we must assume all items have changed. timeline_items = values; reobtain_latest_event = true;