[fractal/fractal-next] timeline: Use streaming api to load timeline
- From: Julian Sparber <jsparber src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [fractal/fractal-next] timeline: Use streaming api to load timeline
- Date: Tue, 15 Feb 2022 13:51:49 +0000 (UTC)
commit b530e11a680003e390188e6ea5cf83bb29551ec8
Author: Julian Sparber <julian sparber net>
Date: Mon Feb 7 18:28:44 2022 +0100
timeline: Use streaming api to load timeline
This load the timeline only once the user opends the room.
This also updates some deps, including the sdk.
Cargo.lock | 169 ++++++++++++---------
Cargo.toml | 3 +-
src/login.rs | 2 +-
src/session/content/room_history/mod.rs | 82 +++++++---
src/session/mod.rs | 11 +-
src/session/room/event.rs | 4 +-
src/session/room/mod.rs | 40 ++---
src/session/room/timeline.rs | 257 ++++++++++++++++++++------------
src/utils.rs | 7 +-
9 files changed, 352 insertions(+), 223 deletions(-)
---
diff --git a/Cargo.lock b/Cargo.lock
index aefdfeb68..c62cb132d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -313,14 +313,14 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "backoff"
-version = "0.3.0"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9fe17f59a06fe8b87a6fc8bf53bb70b3aba76d7685f432487a68cd5552853625"
+checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1"
dependencies = [
"futures-core",
"getrandom 0.2.4",
"instant",
- "pin-project",
+ "pin-project-lite",
"rand 0.8.4",
"tokio",
]
@@ -387,6 +387,15 @@ dependencies = [
"generic-array",
]
+[[package]]
+name = "block-buffer"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
+dependencies = [
+ "generic-array",
+]
+
[[package]]
name = "block-modes"
version = "0.7.0"
@@ -710,20 +719,19 @@ dependencies = [
]
[[package]]
-name = "crypto-mac"
-version = "0.10.1"
+name = "crypto-common"
+version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a"
+checksum = "a4600d695eb3f6ce1cd44e6e291adceb2cc3ab12f20a33777ecd0bf6eba34e06"
dependencies = [
"generic-array",
- "subtle",
]
[[package]]
name = "crypto-mac"
-version = "0.11.1"
+version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714"
+checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a"
dependencies = [
"generic-array",
"subtle",
@@ -745,7 +753,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61"
dependencies = [
"byteorder",
- "digest",
+ "digest 0.9.0",
"rand_core 0.5.1",
"subtle",
"zeroize",
@@ -800,6 +808,17 @@ dependencies = [
"generic-array",
]
+[[package]]
+name = "digest"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cb780dce4f9a8f5c087362b3a4595936b2019e7c8b30f2c3e9a7e94e6ae9837"
+dependencies = [
+ "block-buffer 0.10.2",
+ "crypto-common",
+ "subtle",
+]
+
[[package]]
name = "easy-parallel"
version = "3.2.0"
@@ -825,7 +844,7 @@ dependencies = [
"ed25519",
"rand 0.7.3",
"serde",
- "sha2",
+ "sha2 0.9.9",
"zeroize",
]
@@ -981,6 +1000,7 @@ name = "fractal"
version = "0.0.1"
dependencies = [
"ashpd",
+ "async-stream",
"futures",
"gettext-rs",
"gst-plugin-gtk4",
@@ -1812,7 +1832,7 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f"
dependencies = [
- "digest",
+ "digest 0.9.0",
"hmac 0.10.1",
]
@@ -1822,18 +1842,17 @@ version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15"
dependencies = [
- "crypto-mac 0.10.1",
- "digest",
+ "crypto-mac",
+ "digest 0.9.0",
]
[[package]]
name = "hmac"
-version = "0.11.0"
+version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b"
+checksum = "ddca131f3e7f2ce2df364b57949a9d47915cfbd35e46cfee355ccebbf794d6a2"
dependencies = [
- "crypto-mac 0.11.1",
- "digest",
+ "digest 0.10.2",
]
[[package]]
@@ -2207,6 +2226,15 @@ dependencies = [
"hashbrown",
]
+[[package]]
+name = "lru"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "274353858935c992b13c0ca408752e2121da852d07dec7ce5f108c77dfa14d1f"
+dependencies = [
+ "hashbrown",
+]
+
[[package]]
name = "mac"
version = "0.1.1"
@@ -2263,7 +2291,7 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]]
name = "matrix-qrcode"
version = "0.2.0"
-source =
"git+https://github.com/jsparber/matrix-rust-sdk.git?branch=messages-api#cb8a156b3ac13138398354d75e0b766143fdc660"
+source =
"git+https://github.com/jsparber/matrix-rust-sdk.git?branch=timeline_stream#887986ebce4c72dbf1d2e8198e453162e94ea11f"
dependencies = [
"base64",
"byteorder",
@@ -2271,13 +2299,14 @@ dependencies = [
"qrcode",
"rqrr",
"ruma-identifiers",
+ "ruma-serde",
"thiserror",
]
[[package]]
name = "matrix-sdk"
version = "0.4.1"
-source =
"git+https://github.com/jsparber/matrix-rust-sdk.git?branch=messages-api#cb8a156b3ac13138398354d75e0b766143fdc660"
+source =
"git+https://github.com/jsparber/matrix-rust-sdk.git?branch=timeline_stream#887986ebce4c72dbf1d2e8198e453162e94ea11f"
dependencies = [
"anymap2",
"async-stream",
@@ -2306,14 +2335,15 @@ dependencies = [
[[package]]
name = "matrix-sdk-base"
version = "0.4.1"
-source =
"git+https://github.com/jsparber/matrix-rust-sdk.git?branch=messages-api#cb8a156b3ac13138398354d75e0b766143fdc660"
+source =
"git+https://github.com/jsparber/matrix-rust-sdk.git?branch=timeline_stream#887986ebce4c72dbf1d2e8198e453162e94ea11f"
dependencies = [
"chacha20poly1305",
"dashmap",
+ "futures-channel",
"futures-core",
"futures-util",
- "hmac 0.11.0",
- "lru",
+ "hmac 0.12.0",
+ "lru 0.7.2",
"matrix-sdk-common",
"matrix-sdk-crypto",
"pbkdf2",
@@ -2321,7 +2351,7 @@ dependencies = [
"ruma",
"serde",
"serde_json",
- "sha2",
+ "sha2 0.10.1",
"sled",
"thiserror",
"tokio",
@@ -2332,7 +2362,7 @@ dependencies = [
[[package]]
name = "matrix-sdk-common"
version = "0.4.1"
-source =
"git+https://github.com/jsparber/matrix-rust-sdk.git?branch=messages-api#cb8a156b3ac13138398354d75e0b766143fdc660"
+source =
"git+https://github.com/jsparber/matrix-rust-sdk.git?branch=timeline_stream#887986ebce4c72dbf1d2e8198e453162e94ea11f"
dependencies = [
"async-lock",
"async-trait",
@@ -2342,13 +2372,15 @@ dependencies = [
"serde",
"tokio",
"uuid",
+ "wasm-bindgen",
"wasm-bindgen-futures",
+ "web-sys",
]
[[package]]
name = "matrix-sdk-crypto"
version = "0.4.1"
-source =
"git+https://github.com/jsparber/matrix-rust-sdk.git?branch=messages-api#cb8a156b3ac13138398354d75e0b766143fdc660"
+source =
"git+https://github.com/jsparber/matrix-rust-sdk.git?branch=timeline_stream#887986ebce4c72dbf1d2e8198e453162e94ea11f"
dependencies = [
"aes 0.7.5",
"aes-gcm",
@@ -2359,7 +2391,7 @@ dependencies = [
"dashmap",
"futures-util",
"getrandom 0.2.4",
- "hmac 0.11.0",
+ "hmac 0.12.0",
"matrix-qrcode",
"matrix-sdk-common",
"olm-rs",
@@ -2368,7 +2400,7 @@ dependencies = [
"ruma",
"serde",
"serde_json",
- "sha2",
+ "sha2 0.10.1",
"sled",
"thiserror",
"tracing",
@@ -2862,11 +2894,11 @@ checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5"
[[package]]
name = "pbkdf2"
-version = "0.9.0"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f05894bce6a1ba4be299d0c5f29563e08af2bc18bb7d48313113bed71e904739"
+checksum = "a4628cc3cf953b82edcd3c1388c5715401420ce5524fedbab426bd5aba017434"
dependencies = [
- "crypto-mac 0.11.1",
+ "digest 0.10.2",
]
[[package]]
@@ -2928,26 +2960,6 @@ dependencies = [
"siphasher",
]
-[[package]]
-name = "pin-project"
-version = "1.0.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e"
-dependencies = [
- "pin-project-internal",
-]
-
-[[package]]
-name = "pin-project-internal"
-version = "1.0.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb"
-dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.14",
- "syn 1.0.85",
-]
-
[[package]]
name = "pin-project-lite"
version = "0.2.8"
@@ -3374,13 +3386,13 @@ checksum = "6fa79947f53b20adb909a323d828d0fd744fa9d854792df07913b083bcd4d63b"
dependencies = [
"g2p",
"image",
- "lru",
+ "lru 0.6.6",
]
[[package]]
name = "ruma"
version = "0.4.0"
-source =
"git+https://github.com/ruma/ruma/?rev=fdbc4d6d1dd273c8a6ac95b329943ed8c68df70d#fdbc4d6d1dd273c8a6ac95b329943ed8c68df70d"
+source =
"git+https://github.com/ruma/ruma/?rev=37095f88553b311e7a70adaaabe39976fb8ff71c#37095f88553b311e7a70adaaabe39976fb8ff71c"
dependencies = [
"assign",
"js_int",
@@ -3398,7 +3410,7 @@ dependencies = [
[[package]]
name = "ruma-api"
version = "0.18.5"
-source =
"git+https://github.com/ruma/ruma/?rev=fdbc4d6d1dd273c8a6ac95b329943ed8c68df70d#fdbc4d6d1dd273c8a6ac95b329943ed8c68df70d"
+source =
"git+https://github.com/ruma/ruma/?rev=37095f88553b311e7a70adaaabe39976fb8ff71c#37095f88553b311e7a70adaaabe39976fb8ff71c"
dependencies = [
"bytes",
"http",
@@ -3414,7 +3426,7 @@ dependencies = [
[[package]]
name = "ruma-api-macros"
version = "0.18.5"
-source =
"git+https://github.com/ruma/ruma/?rev=fdbc4d6d1dd273c8a6ac95b329943ed8c68df70d#fdbc4d6d1dd273c8a6ac95b329943ed8c68df70d"
+source =
"git+https://github.com/ruma/ruma/?rev=37095f88553b311e7a70adaaabe39976fb8ff71c#37095f88553b311e7a70adaaabe39976fb8ff71c"
dependencies = [
"proc-macro-crate 1.1.0",
"proc-macro2 1.0.36",
@@ -3425,7 +3437,7 @@ dependencies = [
[[package]]
name = "ruma-client-api"
version = "0.12.3"
-source =
"git+https://github.com/ruma/ruma/?rev=fdbc4d6d1dd273c8a6ac95b329943ed8c68df70d#fdbc4d6d1dd273c8a6ac95b329943ed8c68df70d"
+source =
"git+https://github.com/ruma/ruma/?rev=37095f88553b311e7a70adaaabe39976fb8ff71c#37095f88553b311e7a70adaaabe39976fb8ff71c"
dependencies = [
"assign",
"bytes",
@@ -3445,7 +3457,7 @@ dependencies = [
[[package]]
name = "ruma-common"
version = "0.6.0"
-source =
"git+https://github.com/ruma/ruma/?rev=fdbc4d6d1dd273c8a6ac95b329943ed8c68df70d#fdbc4d6d1dd273c8a6ac95b329943ed8c68df70d"
+source =
"git+https://github.com/ruma/ruma/?rev=37095f88553b311e7a70adaaabe39976fb8ff71c#37095f88553b311e7a70adaaabe39976fb8ff71c"
dependencies = [
"indexmap",
"js_int",
@@ -3460,7 +3472,7 @@ dependencies = [
[[package]]
name = "ruma-events"
version = "0.24.6"
-source =
"git+https://github.com/ruma/ruma/?rev=fdbc4d6d1dd273c8a6ac95b329943ed8c68df70d#fdbc4d6d1dd273c8a6ac95b329943ed8c68df70d"
+source =
"git+https://github.com/ruma/ruma/?rev=37095f88553b311e7a70adaaabe39976fb8ff71c#37095f88553b311e7a70adaaabe39976fb8ff71c"
dependencies = [
"indoc",
"js_int",
@@ -3472,12 +3484,13 @@ dependencies = [
"serde",
"serde_json",
"thiserror",
+ "wildmatch",
]
[[package]]
name = "ruma-events-macros"
version = "0.24.6"
-source =
"git+https://github.com/ruma/ruma/?rev=fdbc4d6d1dd273c8a6ac95b329943ed8c68df70d#fdbc4d6d1dd273c8a6ac95b329943ed8c68df70d"
+source =
"git+https://github.com/ruma/ruma/?rev=37095f88553b311e7a70adaaabe39976fb8ff71c#37095f88553b311e7a70adaaabe39976fb8ff71c"
dependencies = [
"proc-macro-crate 1.1.0",
"proc-macro2 1.0.36",
@@ -3488,7 +3501,7 @@ dependencies = [
[[package]]
name = "ruma-federation-api"
version = "0.3.1"
-source =
"git+https://github.com/ruma/ruma/?rev=fdbc4d6d1dd273c8a6ac95b329943ed8c68df70d#fdbc4d6d1dd273c8a6ac95b329943ed8c68df70d"
+source =
"git+https://github.com/ruma/ruma/?rev=37095f88553b311e7a70adaaabe39976fb8ff71c#37095f88553b311e7a70adaaabe39976fb8ff71c"
dependencies = [
"js_int",
"ruma-api",
@@ -3503,20 +3516,22 @@ dependencies = [
[[package]]
name = "ruma-identifiers"
version = "0.20.0"
-source =
"git+https://github.com/ruma/ruma/?rev=fdbc4d6d1dd273c8a6ac95b329943ed8c68df70d#fdbc4d6d1dd273c8a6ac95b329943ed8c68df70d"
+source =
"git+https://github.com/ruma/ruma/?rev=37095f88553b311e7a70adaaabe39976fb8ff71c#37095f88553b311e7a70adaaabe39976fb8ff71c"
dependencies = [
"percent-encoding",
+ "rand 0.8.4",
"ruma-identifiers-macros",
"ruma-identifiers-validation",
"ruma-serde",
"ruma-serde-macros",
"serde",
+ "uuid",
]
[[package]]
name = "ruma-identifiers-macros"
version = "0.20.0"
-source =
"git+https://github.com/ruma/ruma/?rev=fdbc4d6d1dd273c8a6ac95b329943ed8c68df70d#fdbc4d6d1dd273c8a6ac95b329943ed8c68df70d"
+source =
"git+https://github.com/ruma/ruma/?rev=37095f88553b311e7a70adaaabe39976fb8ff71c#37095f88553b311e7a70adaaabe39976fb8ff71c"
dependencies = [
"quote 1.0.14",
"ruma-identifiers-validation",
@@ -3526,7 +3541,7 @@ dependencies = [
[[package]]
name = "ruma-identifiers-validation"
version = "0.5.0"
-source =
"git+https://github.com/ruma/ruma/?rev=fdbc4d6d1dd273c8a6ac95b329943ed8c68df70d#fdbc4d6d1dd273c8a6ac95b329943ed8c68df70d"
+source =
"git+https://github.com/ruma/ruma/?rev=37095f88553b311e7a70adaaabe39976fb8ff71c#37095f88553b311e7a70adaaabe39976fb8ff71c"
dependencies = [
"thiserror",
]
@@ -3534,8 +3549,9 @@ dependencies = [
[[package]]
name = "ruma-serde"
version = "0.5.0"
-source =
"git+https://github.com/ruma/ruma/?rev=fdbc4d6d1dd273c8a6ac95b329943ed8c68df70d#fdbc4d6d1dd273c8a6ac95b329943ed8c68df70d"
+source =
"git+https://github.com/ruma/ruma/?rev=37095f88553b311e7a70adaaabe39976fb8ff71c#37095f88553b311e7a70adaaabe39976fb8ff71c"
dependencies = [
+ "base64",
"bytes",
"form_urlencoded",
"itoa 0.4.8",
@@ -3548,7 +3564,7 @@ dependencies = [
[[package]]
name = "ruma-serde-macros"
version = "0.5.0"
-source =
"git+https://github.com/ruma/ruma/?rev=fdbc4d6d1dd273c8a6ac95b329943ed8c68df70d#fdbc4d6d1dd273c8a6ac95b329943ed8c68df70d"
+source =
"git+https://github.com/ruma/ruma/?rev=37095f88553b311e7a70adaaabe39976fb8ff71c#37095f88553b311e7a70adaaabe39976fb8ff71c"
dependencies = [
"proc-macro-crate 1.1.0",
"proc-macro2 1.0.36",
@@ -3559,7 +3575,7 @@ dependencies = [
[[package]]
name = "ruma-signatures"
version = "0.9.0"
-source =
"git+https://github.com/ruma/ruma/?rev=fdbc4d6d1dd273c8a6ac95b329943ed8c68df70d#fdbc4d6d1dd273c8a6ac95b329943ed8c68df70d"
+source =
"git+https://github.com/ruma/ruma/?rev=37095f88553b311e7a70adaaabe39976fb8ff71c#37095f88553b311e7a70adaaabe39976fb8ff71c"
dependencies = [
"base64",
"ed25519-dalek",
@@ -3568,7 +3584,7 @@ dependencies = [
"ruma-identifiers",
"ruma-serde",
"serde_json",
- "sha2",
+ "sha2 0.9.9",
"thiserror",
"tracing",
]
@@ -3576,7 +3592,7 @@ dependencies = [
[[package]]
name = "ruma-state-res"
version = "0.4.1"
-source =
"git+https://github.com/ruma/ruma/?rev=fdbc4d6d1dd273c8a6ac95b329943ed8c68df70d#fdbc4d6d1dd273c8a6ac95b329943ed8c68df70d"
+source =
"git+https://github.com/ruma/ruma/?rev=37095f88553b311e7a70adaaabe39976fb8ff71c#37095f88553b311e7a70adaaabe39976fb8ff71c"
dependencies = [
"itertools",
"js_int",
@@ -3652,7 +3668,7 @@ dependencies = [
"num",
"rand 0.8.4",
"serde",
- "sha2",
+ "sha2 0.9.9",
"zbus 1.9.1",
"zbus_macros 1.9.1",
"zvariant 2.10.0",
@@ -3781,13 +3797,24 @@ version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
dependencies = [
- "block-buffer",
+ "block-buffer 0.9.0",
"cfg-if 1.0.0",
"cpufeatures",
- "digest",
+ "digest 0.9.0",
"opaque-debug",
]
+[[package]]
+name = "sha2"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99c3bd8169c58782adad9290a9af5939994036b76187f7b4f0e6de91dbbfc0ec"
+dependencies = [
+ "cfg-if 1.0.0",
+ "cpufeatures",
+ "digest 0.10.2",
+]
+
[[package]]
name = "sharded-slab"
version = "0.1.4"
diff --git a/Cargo.toml b/Cargo.toml
index c6e78d183..70b57a85a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,6 +13,7 @@ incremental = false
codegen-units = 16
[dependencies]
+async-stream = "0.3"
log = "0.4"
mime = "0.3.16"
tracing-subscriber = "0.3"
@@ -59,7 +60,7 @@ version = "0.1.0"
[dependencies.matrix-sdk]
git = "https://github.com/jsparber/matrix-rust-sdk.git"
-branch = "messages-api"
+branch = "timeline_stream"
features = [
"socks",
"encryption",
diff --git a/src/login.rs b/src/login.rs
index c477ea4ca..2ee7f2703 100644
--- a/src/login.rs
+++ b/src/login.rs
@@ -331,7 +331,7 @@ impl Login {
self.freeze();
let handle: JoinHandle<MatrixResult<_>> = spawn_tokio!(async move {
- let client = Client::new(homeserver_clone)?;
+ let client = Client::new(homeserver_clone).await?;
Ok(client
.send(
get_supported_versions::Request::new(),
diff --git a/src/session/content/room_history/mod.rs b/src/session/content/room_history/mod.rs
index aaefc2fc1..834dfc869 100644
--- a/src/session/content/room_history/mod.rs
+++ b/src/session/content/room_history/mod.rs
@@ -29,6 +29,7 @@ use crate::{
room::{Item, Room, RoomType, Timeline, TimelineState},
user::UserExt,
},
+ spawn,
};
mod imp {
@@ -76,6 +77,7 @@ mod imp {
pub error: TemplateChild<adw::StatusPage>,
#[template_child]
pub stack: TemplateChild<gtk::Stack>,
+ pub is_loading: Cell<bool>,
}
#[glib::object_subclass]
@@ -245,13 +247,13 @@ mod imp {
} else {
obj.set_sticky(adj.value() + adj.page_size() == adj.upper());
}
- obj.load_more_messages(adj);
+ obj.start_loading();
}));
- adj.connect_upper_notify(clone!(@weak obj => move |adj| {
+ adj.connect_upper_notify(clone!(@weak obj => move |_| {
if obj.sticky() {
obj.scroll_down();
}
- obj.load_more_messages(adj);
+ obj.start_loading();
}));
let key_events = gtk::EventControllerKey::new();
@@ -318,19 +320,15 @@ impl RoomHistory {
return;
}
- if let Some(category_handler) = priv_.category_handler.take() {
- if let Some(room) = self.room() {
+ if let Some(room) = self.room() {
+ if let Some(category_handler) = priv_.category_handler.take() {
room.disconnect(category_handler);
}
- }
- if let Some(empty_timeline_handler) = priv_.empty_timeline_handler.take() {
- if let Some(room) = self.room() {
+ if let Some(empty_timeline_handler) = priv_.empty_timeline_handler.take() {
room.timeline().disconnect(empty_timeline_handler);
}
- }
- if let Some(room) = self.room() {
if let Some(state_timeline_handler) = priv_.state_timeline_handler.take() {
room.timeline().disconnect(state_timeline_handler);
}
@@ -373,10 +371,10 @@ impl RoomHistory {
.map(|room| gtk::NoSelection::new(Some(room.timeline())));
priv_.listview.set_model(model.as_ref());
+ priv_.is_loading.set(false);
priv_.room.replace(room);
- let adj = priv_.listview.vadjustment().unwrap();
- self.load_more_messages(&adj);
self.update_view();
+ self.start_loading();
self.update_room_state();
self.notify("room");
self.notify("empty");
@@ -537,17 +535,57 @@ impl RoomHistory {
}
}
- fn load_more_messages(&self, adj: >k::Adjustment) {
+ fn need_messages(&self) -> bool {
+ let adj = self.imp().listview.vadjustment().unwrap();
// Load more messages when the user gets close to the end of the known room
// history Use the page size twice to detect if the user gets close to
- // the end
- if let Some(room) = self.room() {
- if adj.value() < adj.page_size() * 2.0
- || adj.upper() <= adj.page_size() / 2.0
- || room.timeline().is_empty()
- {
- room.timeline().load_previous_events();
+ // the endload_timeline
+ adj.value() < adj.page_size() * 2.0 || adj.upper() <= adj.page_size() / 2.0
+ }
+
+ fn start_loading(&self) {
+ let priv_ = self.imp();
+ if !priv_.is_loading.get() {
+ let room = if let Some(room) = self.room() {
+ room
+ } else {
+ return;
+ };
+
+ if !self.need_messages() && !room.timeline().is_empty() {
+ return;
}
+
+ priv_.is_loading.set(true);
+
+ let obj_weak = self.downgrade();
+ spawn!(async move {
+ loop {
+ // We don't want to hold a strong ref to `obj` on `await`
+ let need = if let Some(obj) = obj_weak.upgrade() {
+ if obj.room().as_ref() == Some(&room) {
+ obj.need_messages() || room.timeline().is_empty()
+ } else {
+ return;
+ }
+ } else {
+ return;
+ };
+
+ if need {
+ if !room.timeline().load().await {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+
+ // Remove the task
+ if let Some(obj) = obj_weak.upgrade() {
+ obj.imp().is_loading.set(false);
+ }
+ });
}
}
@@ -585,9 +623,7 @@ impl RoomHistory {
}
fn try_again(&self) {
- if let Some(room) = self.room() {
- room.timeline().load_previous_events();
- }
+ self.start_loading();
}
}
diff --git a/src/session/mod.rs b/src/session/mod.rs
index 96e5fa702..f44a29505 100644
--- a/src/session/mod.rs
+++ b/src/session/mod.rs
@@ -40,7 +40,6 @@ use matrix_sdk::{
assign,
identifiers::RoomId,
},
- uuid::Uuid,
Client, Error as MatrixError, HttpError,
};
use rand::{distributions::Alphanumeric, thread_rng, Rng};
@@ -293,11 +292,7 @@ impl Session {
) {
self.imp().logout_on_dispose.set(true);
let mut path = glib::user_data_dir();
- path.push(
- &Uuid::new_v4()
- .to_hyphenated()
- .encode_lower(&mut Uuid::encode_buffer()),
- );
+ path.push(glib::uuid_string_random().as_str());
let handle = spawn_tokio!(async move {
let passphrase: String = {
@@ -322,7 +317,7 @@ impl Session {
config
};
- let client = Client::new_with_config(homeserver.clone(), config).unwrap();
+ let client = Client::new_with_config(homeserver.clone(), config).await?;
let response = client
.login(&username, &password, None, Some("Fractal Next"))
.await;
@@ -371,7 +366,7 @@ impl Session {
.passphrase(session.secret.passphrase.clone())
.store_path(session.path.clone());
- let client = Client::new_with_config(session.homeserver.clone(), config).unwrap();
+ let client = Client::new_with_config(session.homeserver.clone(), config).await?;
client
.restore_login(matrix_sdk::Session {
user_id: session.user_id.clone(),
diff --git a/src/session/room/event.rs b/src/session/room/event.rs
index 0e1234755..1c1e6c3df 100644
--- a/src/session/room/event.rs
+++ b/src/session/room/event.rs
@@ -16,7 +16,7 @@ use matrix_sdk::{
AnyMessageEventContent, AnySyncMessageEvent, AnySyncRoomEvent, AnySyncStateEvent,
Unsigned,
},
- identifiers::{EventId, UserId},
+ identifiers::{EventId, TransactionId, UserId},
MilliSecondsSinceUnixEpoch,
},
Error as MatrixError,
@@ -255,7 +255,7 @@ impl Event {
}
}
- pub fn matrix_transaction_id(&self) -> Option<String> {
+ pub fn matrix_transaction_id(&self) -> Option<Box<TransactionId>> {
self.imp()
.pure_event
.borrow()
diff --git a/src/session/room/mod.rs b/src/session/room/mod.rs
index ff201db9f..809a26c9c 100644
--- a/src/session/room/mod.rs
+++ b/src/session/room/mod.rs
@@ -35,11 +35,10 @@ use matrix_sdk::{
AnySyncMessageEvent, AnySyncRoomEvent, AnySyncStateEvent, EventType, SyncMessageEvent,
Unsigned,
},
- identifiers::{EventId, RoomId, UserId},
+ identifiers::{EventId, RoomId, TransactionId, UserId},
serde::Raw,
MilliSecondsSinceUnixEpoch,
},
- uuid::Uuid,
};
use serde_json::value::RawValue;
@@ -75,7 +74,7 @@ mod imp {
use super::*;
- #[derive(Debug, Default)]
+ #[derive(Default)]
pub struct Room {
pub room_id: OnceCell<Box<RoomId>>,
pub matrix_room: RefCell<Option<MatrixRoom>>,
@@ -318,6 +317,14 @@ mod imp {
obj.bind_property("display-name", obj.avatar(), "display-name")
.flags(glib::BindingFlags::SYNC_CREATE)
.build();
+
+ // Load the room history when idle
+ spawn!(
+ glib::source::PRIORITY_LOW,
+ clone!(@weak obj => async move {
+ obj.timeline().load().await;
+ })
+ );
}
}
}
@@ -491,30 +498,28 @@ impl Room {
MatrixRoom::Joined(room) => match category {
RoomType::Invited => Ok(()),
RoomType::Favorite => {
- room.set_tag(TagName::Favorite.as_ref(), TagInfo::new())
- .await?;
+ room.set_tag(TagName::Favorite, TagInfo::new()).await?;
if previous_category == RoomType::LowPriority {
- room.remove_tag(TagName::LowPriority.as_ref()).await?;
+ room.remove_tag(TagName::LowPriority).await?;
}
Ok(())
}
RoomType::Normal => {
match previous_category {
RoomType::Favorite => {
- room.remove_tag(TagName::Favorite.as_ref()).await?;
+ room.remove_tag(TagName::Favorite).await?;
}
RoomType::LowPriority => {
- room.remove_tag(TagName::LowPriority.as_ref()).await?;
+ room.remove_tag(TagName::LowPriority).await?;
}
_ => {}
}
Ok(())
}
RoomType::LowPriority => {
- room.set_tag(TagName::LowPriority.as_ref(), TagInfo::new())
- .await?;
+ room.set_tag(TagName::LowPriority, TagInfo::new()).await?;
if previous_category == RoomType::Favorite {
- room.remove_tag(TagName::Favorite.as_ref()).await?;
+ room.remove_tag(TagName::Favorite).await?;
}
Ok(())
}
@@ -793,7 +798,7 @@ impl Room {
self.notify("inviter");
}
- /// Add new events to the timeline
+ /// Update the room state based on the new sync response
pub fn append_events(&self, batch: Vec<Event>) {
let priv_ = self.imp();
@@ -832,7 +837,6 @@ impl Room {
}
}
- priv_.timeline.get().unwrap().append(batch);
priv_.latest_change.replace(latest_change);
self.notify("latest-change");
self.emit_by_name::<()>("order-changed", &[]);
@@ -907,7 +911,7 @@ impl Room {
}
/// Send the given `event` in this room, with the temporary ID `txn_id`.
- fn send_room_message_event(&self, event: AnySyncMessageEvent, txn_id: Uuid) {
+ fn send_room_message_event(&self, event: AnySyncMessageEvent, txn_id: Box<TransactionId>) {
if let MatrixRoom::Joined(matrix_room) = self.matrix_room() {
let content = event.content();
let json = serde_json::to_string(&AnySyncRoomEvent::Message(event)).unwrap();
@@ -918,9 +922,10 @@ impl Room {
.timeline
.get()
.unwrap()
- .append_pending(txn_id, event);
+ .append_pending(&txn_id, event);
- let handle = spawn_tokio!(async move { matrix_room.send(content, Some(txn_id)).await });
+ let handle =
+ spawn_tokio!(async move { matrix_room.send(content, Some(&txn_id)).await });
spawn!(
glib::PRIORITY_DEFAULT_IDLE,
@@ -989,7 +994,7 @@ impl Room {
.timeline
.get()
.unwrap()
- .append_pending(txn_id, event);
+ .append_pending(&txn_id, event);
let handle = spawn_tokio!(async move {
matrix_room
@@ -1103,7 +1108,6 @@ impl Room {
pub fn handle_left_response(&self, response_room: LeftRoom) {
self.set_matrix_room(self.session().client().get_room(self.room_id()).unwrap());
-
self.append_events(
response_room
.timeline
diff --git a/src/session/room/timeline.rs b/src/session/room/timeline.rs
index 899e59e99..282f532ae 100644
--- a/src/session/room/timeline.rs
+++ b/src/session/room/timeline.rs
@@ -1,16 +1,19 @@
-use std::collections::HashMap;
+use std::{
+ collections::{HashMap, HashSet, VecDeque},
+ pin::Pin,
+ sync::Arc,
+};
-use gtk::{gio, glib, glib::clone, prelude::*, subclass::prelude::*};
+use futures::{lock::Mutex, pin_mut, Stream, StreamExt};
+use gtk::{gio, glib, prelude::*, subclass::prelude::*};
use log::{error, warn};
use matrix_sdk::{
+ deserialized_responses::SyncRoomEvent,
+ room::Room as MatrixRoom,
ruma::{
- api::client::r0::message::get_message_events::Direction,
- events::{
- room::message::MessageType, AnySyncMessageEvent, AnySyncRoomEvent, AnySyncStateEvent,
- },
- identifiers::EventId,
+ events::{room::message::MessageType, AnySyncMessageEvent, AnySyncRoomEvent},
+ identifiers::{EventId, TransactionId},
},
- uuid::Uuid,
Error as MatrixError,
};
@@ -20,7 +23,7 @@ use crate::{
user::UserExt,
verification::{IdentityVerification, VERIFICATION_CREATION_TIMEOUT},
},
- spawn, spawn_tokio,
+ spawn_tokio,
};
#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy, glib::Enum)]
@@ -40,11 +43,12 @@ impl Default for TimelineState {
}
}
+const MAX_BATCH_SIZE: usize = 20;
+type BackwardStream =
+ Pin<Box<dyn Stream<Item = Vec<matrix_sdk::Result<SyncRoomEvent>>> + 'static + Send>>;
+
mod imp {
- use std::{
- cell::{Cell, RefCell},
- collections::{HashSet, VecDeque},
- };
+ use std::cell::{Cell, RefCell};
use glib::object::WeakRef;
use once_cell::{sync::Lazy, unsync::OnceCell};
@@ -62,13 +66,13 @@ mod imp {
pub event_map: RefCell<HashMap<Box<EventId>, Event>>,
/// Maps the temporary `EventId` of the pending Event to the real
/// `EventId`
- pub pending_events: RefCell<HashMap<String, Box<EventId>>>,
+ pub pending_events: RefCell<HashMap<Box<TransactionId>, Box<EventId>>>,
/// A Hashset of `EventId`s that where just redacted.
pub redacted_events: RefCell<HashSet<Box<EventId>>>,
- pub oldest_event: RefCell<Option<Box<EventId>>>,
pub state: Cell<TimelineState>,
/// The most recent verification request event
pub verification: RefCell<Option<IdentityVerification>>,
+ pub backward_stream: Arc<Mutex<Option<BackwardStream>>>,
}
#[glib::object_subclass]
@@ -510,8 +514,92 @@ impl Timeline {
}
}
+ /// Load the timeline
+ /// This function should also be called to load more events
+ /// Returns `true` when messages where successfully added
+ pub async fn load(&self) -> bool {
+ let priv_ = self.imp();
+
+ if matches!(
+ self.state(),
+ TimelineState::Loading | TimelineState::Complete
+ ) {
+ return false;
+ }
+
+ self.set_state(TimelineState::Loading);
+ self.add_loading_spinner();
+
+ let matrix_room = self.room().matrix_room();
+ let room_weak = self.downgrade().into();
+ let backward_stream = priv_.backward_stream.clone();
+
+ let handle: tokio::task::JoinHandle<matrix_sdk::Result<_>> = spawn_tokio!(async move {
+ let mut backward_stream_guard = backward_stream.lock().await;
+ if backward_stream_guard.is_none() {
+ backward_stream_guard
+ .replace(create_streams_handler(room_weak, matrix_room).await?);
+ }
+
+ Ok(backward_stream_guard.as_mut().unwrap().next().await)
+ });
+
+ match handle.await.unwrap() {
+ Ok(Some(events)) => {
+ let events: Vec<Event> = events
+ .into_iter()
+ .filter_map(|event| match event {
+ Ok(event) => Some(event),
+ Err(error) => {
+ error!("Failed to load messages: {}", error);
+ None
+ }
+ })
+ .map(|event| Event::new(event, &self.room()))
+ .collect();
+
+ self.remove_loading_spinner();
+ if events.is_empty() {
+ self.set_state(TimelineState::Error);
+ return false;
+ }
+ self.set_state(TimelineState::Ready);
+ self.prepend(events);
+ true
+ }
+ Ok(None) => {
+ self.remove_loading_spinner();
+ self.set_state(TimelineState::Complete);
+ false
+ }
+ Err(error) => {
+ error!("Failed to load timeline: {}", error);
+ self.set_state(TimelineState::Error);
+ self.remove_loading_spinner();
+ false
+ }
+ }
+ }
+
+ async fn clear(&self) {
+ let priv_ = self.imp();
+ // Remove backward stream so that we create new streams
+ let mut backward_stream = priv_.backward_stream.lock().await;
+ backward_stream.take();
+
+ let length = priv_.list.borrow().len();
+ priv_.relates_to_events.replace(HashMap::new());
+ priv_.list.replace(VecDeque::new());
+ priv_.event_map.replace(HashMap::new());
+ priv_.pending_events.replace(HashMap::new());
+ priv_.redacted_events.replace(HashSet::new());
+
+ self.notify("empty");
+ self.upcast_ref::<gio::ListModel>()
+ .items_changed(0, length as u32, 0);
+ }
+
/// Append the new events
- // TODO: This should be lazy, for inspiration see:
https://blogs.gnome.org/ebassi/documentation/lazy-loading/
pub fn append(&self, batch: Vec<Event>) {
let priv_ = self.imp();
@@ -527,12 +615,6 @@ impl Timeline {
// multiple times
list.reserve(batch.len());
- if list.is_empty() {
- priv_
- .oldest_event
- .replace(batch.first().as_ref().map(|event| event.matrix_event_id()));
- }
-
list.len()
};
@@ -578,7 +660,7 @@ impl Timeline {
}
/// Append an event that wasn't yet fully sent and received via a sync
- pub fn append_pending(&self, txn_id: Uuid, event: Event) {
+ pub fn append_pending(&self, txn_id: &TransactionId, event: Event) {
let priv_ = self.imp();
priv_
@@ -589,7 +671,7 @@ impl Timeline {
priv_
.pending_events
.borrow_mut()
- .insert(txn_id.to_string(), event.matrix_event_id());
+ .insert(txn_id.to_owned(), event.matrix_event_id());
let index = {
let mut list = priv_.list.borrow_mut();
@@ -634,7 +716,7 @@ impl Timeline {
let handle =
spawn_tokio!(async move { matrix_room.event(event_id_clone.as_ref()).await });
match handle.await.unwrap() {
- Ok(room_event) => Ok(Event::new(room_event.event.into(), &room)),
+ Ok(room_event) => Ok(Event::new(room_event.into(), &room)),
Err(error) => {
// TODO: Retry on connection error?
warn!("Could not fetch event {}: {}", event_id, error);
@@ -645,15 +727,10 @@ impl Timeline {
}
/// Prepends a batch of events
- // TODO: This should be lazy, see: https://blogs.gnome.org/ebassi/documentation/lazy-loading/
pub fn prepend(&self, batch: Vec<Event>) {
let priv_ = self.imp();
let mut added = batch.len();
- priv_
- .oldest_event
- .replace(batch.last().as_ref().map(|event| event.matrix_event_id()));
-
{
let mut hidden_events: Vec<Event> = vec![];
// Extend the size of the list so that rust doesn't need to reallocate memory
@@ -712,9 +789,6 @@ impl Timeline {
|| (priv_.list.borrow().len() == 1 && self.state() == TimelineState::Loading)
}
- fn oldest_event(&self) -> Option<Box<EventId>> {
- self.imp().oldest_event.borrow().clone()
- }
fn add_loading_spinner(&self) {
self.imp()
.list
@@ -728,68 +802,6 @@ impl Timeline {
self.upcast_ref::<gio::ListModel>().items_changed(0, 1, 0);
}
- pub fn load_previous_events(&self) {
- if matches!(
- self.state(),
- TimelineState::Loading | TimelineState::Complete
- ) {
- return;
- }
-
- self.set_state(TimelineState::Loading);
- self.add_loading_spinner();
-
- let matrix_room = self.room().matrix_room();
- let last_event = self.oldest_event();
- let contains_last_event = last_event.is_some();
-
- let handle = spawn_tokio!(async move {
- matrix_room
- .messages(last_event.as_deref(), None, 20, Direction::Backward)
- .await
- });
-
- spawn!(
- glib::PRIORITY_LOW,
- clone!(@weak self as obj => async move {
- obj.remove_loading_spinner();
-
- // FIXME: If the request fails it's automatically restarted because the added events (none),
didn't fill the screen.
- // We should block the loading for some time before retrying
- match handle.await.unwrap() {
- Ok(Some(events)) => {
- let events: Vec<Event> = if contains_last_event {
- events
- .into_iter()
- .skip(1)
- .map(|event| Event::new(event, &obj.room())).collect()
- } else {
- events
- .into_iter()
- .map(|event| Event::new(event, &obj.room())).collect()
- };
-
- if events.iter().any(|event| matches!(event.matrix_event(),
Some(AnySyncRoomEvent::State(AnySyncStateEvent::RoomCreate(_))))) {
- obj.set_state(TimelineState::Complete);
- } else {
- obj.set_state(TimelineState::Ready);
- }
-
- obj.prepend(events);
- },
- Ok(None) => {
- error!("The start event wasn't found in the timeline for room {}.",
obj.room().room_id());
- obj.set_state(TimelineState::Error);
- },
- Err(error) => {
- error!("Couldn't load previous events for room {}: {}", error,
obj.room().room_id());
- obj.set_state(TimelineState::Error);
- }
- }
- })
- );
- }
-
fn set_verification(&self, verification: IdentityVerification) {
self.imp().verification.replace(Some(verification));
self.notify("verification");
@@ -899,3 +911,58 @@ impl Timeline {
}
}
}
+
+async fn create_streams_handler(
+ timeline: glib::SendWeakRef<Timeline>,
+ matrix_room: MatrixRoom,
+) -> matrix_sdk::Result<BackwardStream> {
+ let (forward_stream, backward_stream) = matrix_room.timeline().await?;
+
+ tokio::spawn(async move {
+ handle_forward_stream(timeline, forward_stream).await;
+ });
+
+ Ok(Box::pin(backward_stream.ready_chunks(MAX_BATCH_SIZE)))
+}
+
+async fn handle_forward_stream(
+ timeline: glib::SendWeakRef<Timeline>,
+ stream: impl Stream<Item = SyncRoomEvent>,
+) {
+ let stream = stream.ready_chunks(MAX_BATCH_SIZE);
+ pin_mut!(stream);
+
+ while let Some(events) = stream.next().await {
+ let timeline = timeline.clone();
+ let (sender, receiver) = futures::channel::oneshot::channel();
+ let ctx = glib::MainContext::default();
+ ctx.spawn(async move {
+ let result = if let Some(timeline) = timeline.upgrade() {
+ timeline.append(
+ events
+ .into_iter()
+ .map(|event| Event::new(event, &timeline.room()))
+ .collect(),
+ );
+
+ true
+ } else {
+ false
+ };
+ sender.send(result).unwrap();
+ });
+
+ if !receiver.await.unwrap() {
+ break;
+ }
+ }
+
+ let ctx = glib::MainContext::default();
+ ctx.spawn(async move {
+ crate::spawn!(async move {
+ if let Some(timeline) = timeline.upgrade() {
+ timeline.clear().await;
+ };
+ });
+ });
+}
diff --git a/src/utils.rs b/src/utils.rs
index 1ff7e3860..450ea9aab 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -69,8 +69,7 @@ use gtk::{
};
use matrix_sdk::{
media::MediaType,
- ruma::{EventId, UInt},
- uuid::Uuid,
+ ruma::{EventId, TransactionId, UInt},
};
use mime::Mime;
@@ -209,8 +208,8 @@ pub fn filename_for_mime(mime_type: Option<&str>, fallback: Option<mime::Name>)
///
/// Returns a `(transaction_id, event_id)` tuple. The `event_id` is derived from
/// the `transaction_id`.
-pub fn pending_event_ids() -> (Uuid, Box<EventId>) {
- let txn_id = Uuid::new_v4();
+pub fn pending_event_ids() -> (Box<TransactionId>, Box<EventId>) {
+ let txn_id = TransactionId::new();
let event_id = EventId::parse(format!("${}:fractal.gnome.org", txn_id)).unwrap();
(txn_id, event_id)
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]