diff --git a/Cargo.lock b/Cargo.lock index dd89c56d..7c01e9db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2627,9 +2627,9 @@ dependencies = [ [[package]] name = "indexed_db_futures" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0704b71f13f81b5933d791abf2de26b33c40935143985220299a357721166706" +checksum = "43315957678a70eb21fb0d2384fe86dde0d6c859a01e24ce127eb65a0143d28c" dependencies = [ "accessory", "cfg-if", @@ -3137,7 +3137,7 @@ dependencies = [ [[package]] name = "matrix-sdk" version = "0.7.1" -source = "git+https://github.com/matrix-org/matrix-rust-sdk.git?rev=7f4e79e2a3bb07fa189c62987a06b3f78f2e4286#7f4e79e2a3bb07fa189c62987a06b3f78f2e4286" +source = "git+https://github.com/zecakeh/matrix-rust-sdk.git?rev=2837564fea7018dfc1e355f6cc051e35ce86ff66#2837564fea7018dfc1e355f6cc051e35ce86ff66" dependencies = [ "anymap2", "aquamarine", @@ -3187,7 +3187,7 @@ dependencies = [ [[package]] name = "matrix-sdk-base" version = "0.7.0" -source = "git+https://github.com/matrix-org/matrix-rust-sdk.git?rev=7f4e79e2a3bb07fa189c62987a06b3f78f2e4286#7f4e79e2a3bb07fa189c62987a06b3f78f2e4286" +source = "git+https://github.com/zecakeh/matrix-rust-sdk.git?rev=2837564fea7018dfc1e355f6cc051e35ce86ff66#2837564fea7018dfc1e355f6cc051e35ce86ff66" dependencies = [ "as_variant", "async-trait", @@ -3211,7 +3211,7 @@ dependencies = [ [[package]] name = "matrix-sdk-common" version = "0.7.0" -source = "git+https://github.com/matrix-org/matrix-rust-sdk.git?rev=7f4e79e2a3bb07fa189c62987a06b3f78f2e4286#7f4e79e2a3bb07fa189c62987a06b3f78f2e4286" +source = "git+https://github.com/zecakeh/matrix-rust-sdk.git?rev=2837564fea7018dfc1e355f6cc051e35ce86ff66#2837564fea7018dfc1e355f6cc051e35ce86ff66" dependencies = [ "async-trait", "futures-core", @@ -3232,7 +3232,7 @@ dependencies = [ [[package]] name = "matrix-sdk-crypto" version = "0.7.2" -source = "git+https://github.com/matrix-org/matrix-rust-sdk.git?rev=7f4e79e2a3bb07fa189c62987a06b3f78f2e4286#7f4e79e2a3bb07fa189c62987a06b3f78f2e4286" +source = "git+https://github.com/zecakeh/matrix-rust-sdk.git?rev=2837564fea7018dfc1e355f6cc051e35ce86ff66#2837564fea7018dfc1e355f6cc051e35ce86ff66" dependencies = [ "aes", "as_variant", @@ -3273,7 +3273,7 @@ dependencies = [ [[package]] name = "matrix-sdk-indexeddb" version = "0.7.0" -source = "git+https://github.com/matrix-org/matrix-rust-sdk.git?rev=7f4e79e2a3bb07fa189c62987a06b3f78f2e4286#7f4e79e2a3bb07fa189c62987a06b3f78f2e4286" +source = "git+https://github.com/zecakeh/matrix-rust-sdk.git?rev=2837564fea7018dfc1e355f6cc051e35ce86ff66#2837564fea7018dfc1e355f6cc051e35ce86ff66" dependencies = [ "anyhow", "async-trait", @@ -3301,7 +3301,7 @@ dependencies = [ [[package]] name = "matrix-sdk-qrcode" version = "0.7.1" -source = "git+https://github.com/matrix-org/matrix-rust-sdk.git?rev=7f4e79e2a3bb07fa189c62987a06b3f78f2e4286#7f4e79e2a3bb07fa189c62987a06b3f78f2e4286" +source = "git+https://github.com/zecakeh/matrix-rust-sdk.git?rev=2837564fea7018dfc1e355f6cc051e35ce86ff66#2837564fea7018dfc1e355f6cc051e35ce86ff66" dependencies = [ "byteorder", "qrcode", @@ -3313,7 +3313,7 @@ dependencies = [ [[package]] name = "matrix-sdk-sqlite" version = "0.7.1" -source = "git+https://github.com/matrix-org/matrix-rust-sdk.git?rev=7f4e79e2a3bb07fa189c62987a06b3f78f2e4286#7f4e79e2a3bb07fa189c62987a06b3f78f2e4286" +source = "git+https://github.com/zecakeh/matrix-rust-sdk.git?rev=2837564fea7018dfc1e355f6cc051e35ce86ff66#2837564fea7018dfc1e355f6cc051e35ce86ff66" dependencies = [ "async-trait", "deadpool-sqlite", @@ -3335,7 +3335,7 @@ dependencies = [ [[package]] name = "matrix-sdk-store-encryption" version = "0.7.0" -source = "git+https://github.com/matrix-org/matrix-rust-sdk.git?rev=7f4e79e2a3bb07fa189c62987a06b3f78f2e4286#7f4e79e2a3bb07fa189c62987a06b3f78f2e4286" +source = "git+https://github.com/zecakeh/matrix-rust-sdk.git?rev=2837564fea7018dfc1e355f6cc051e35ce86ff66#2837564fea7018dfc1e355f6cc051e35ce86ff66" dependencies = [ "base64", "blake3", @@ -3354,7 +3354,7 @@ dependencies = [ [[package]] name = "matrix-sdk-ui" version = "0.7.0" -source = "git+https://github.com/matrix-org/matrix-rust-sdk.git?rev=7f4e79e2a3bb07fa189c62987a06b3f78f2e4286#7f4e79e2a3bb07fa189c62987a06b3f78f2e4286" +source = "git+https://github.com/zecakeh/matrix-rust-sdk.git?rev=2837564fea7018dfc1e355f6cc051e35ce86ff66#2837564fea7018dfc1e355f6cc051e35ce86ff66" dependencies = [ "as_variant", "async-once-cell", @@ -3382,6 +3382,7 @@ dependencies = [ "serde_json", "thiserror", "tokio", + "tokio-stream", "tracing", "unicode-normalization", ] diff --git a/Cargo.toml b/Cargo.toml index d1b4abc4..b08bfb33 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,8 +66,8 @@ shumate = { package = "libshumate", version = "0.6" } sourceview = { package = "sourceview5", version = "0.9" } [dependencies.matrix-sdk] -git = "https://github.com/matrix-org/matrix-rust-sdk.git" -rev = "7f4e79e2a3bb07fa189c62987a06b3f78f2e4286" +git = "https://github.com/zecakeh/matrix-rust-sdk.git" +rev = "2837564fea7018dfc1e355f6cc051e35ce86ff66" features = [ "socks", "sso-login", @@ -76,8 +76,8 @@ features = [ ] [dependencies.matrix-sdk-ui] -git = "https://github.com/matrix-org/matrix-rust-sdk.git" -rev = "7f4e79e2a3bb07fa189c62987a06b3f78f2e4286" +git = "https://github.com/zecakeh/matrix-rust-sdk.git" +rev = "2837564fea7018dfc1e355f6cc051e35ce86ff66" default-features = false features = ["e2e-encryption", "native-tls"] diff --git a/src/session/model/room/mod.rs b/src/session/model/room/mod.rs index f9334c8f..4927637f 100644 --- a/src/session/model/room/mod.rs +++ b/src/session/model/room/mod.rs @@ -21,7 +21,6 @@ use matrix_sdk::{ use ruma::{ api::client::error::{ErrorKind, RetryAfter}, events::{ - receipt::{ReceiptEventContent, ReceiptType}, room::{ avatar::RoomAvatarEventContent, encryption::SyncRoomEncryptionEvent, guest_access::GuestAccess, history_visibility::HistoryVisibility, @@ -217,7 +216,6 @@ mod imp { #[property(get, builder(HistoryVisibilityValue::default()))] pub history_visibility: Cell, pub typing_drop_guard: OnceCell, - pub receipts_drop_guard: OnceCell, } #[glib::object_subclass] @@ -519,7 +517,6 @@ impl Room { self.load_predecessor(); self.load_tombstone(); self.load_category(); - self.set_up_receipts(); self.set_up_typing(); self.init_timeline(); self.set_up_is_encrypted(); @@ -628,15 +625,14 @@ impl Room { } fn init_timeline(&self) { - let timeline = Timeline::new(self); - self.imp().timeline.set(timeline.clone()).unwrap(); + let timeline = self.imp().timeline.get_or_init(|| Timeline::new(self)); - timeline.sdk_items().connect_items_changed(clone!( + timeline.connect_read_change_trigger(clone!( #[weak(rename_to = obj)] self, - move |_, _, _, _| { - spawn!(async move { - obj.update_is_read().await; + move |_| { + spawn!(glib::Priority::DEFAULT_IDLE, async move { + obj.handle_read_change_trigger().await }); } )); @@ -1164,59 +1160,6 @@ impl Room { imp.typing_drop_guard.set(drop_guard).unwrap(); } - /// Start listening to read receipts events. - fn set_up_receipts(&self) { - let imp = self.imp(); - if imp.receipts_drop_guard.get().is_some() { - // The event handler is already set up. - return; - } - - // Listen to changes in the read receipts. - let matrix_room = self.matrix_room(); - let room_weak = glib::SendWeakRef::from(self.downgrade()); - let handle = matrix_room.add_event_handler( - move |event: SyncEphemeralRoomEvent| { - let room_weak = room_weak.clone(); - async move { - let ctx = glib::MainContext::default(); - ctx.spawn(async move { - spawn!(async move { - if let Some(obj) = room_weak.upgrade() { - obj.handle_receipt_event(event.content) - } - }); - }); - } - }, - ); - - let drop_guard = matrix_room.client().event_handler_drop_guard(handle); - imp.receipts_drop_guard.set(drop_guard).unwrap(); - } - - fn handle_receipt_event(&self, content: ReceiptEventContent) { - let Some(session) = self.session() else { - return; - }; - let own_user_id = session.user_id(); - - for (_event_id, receipts) in content.iter() { - if let Some(users) = receipts.get(&ReceiptType::Read) { - if let Some(receipt) = users.get(own_user_id) { - tracing::trace!("{}: Got own receipt: {receipt:?}", self.human_readable_id()); - spawn!(clone!( - #[weak(rename_to = obj)] - self, - async move { - obj.update_is_read().await; - } - )); - } - } - } - } - fn handle_typing_event(&self, content: TypingEventContent) { let Some(session) = self.session() else { return; @@ -1317,8 +1260,9 @@ impl Room { self.notify_highlight(); } - async fn update_is_read(&self) { - tracing::trace!("{}::update_is_read", self.human_readable_id()); + /// Handle the trigger emitted when a read change might have occurred. + async fn handle_read_change_trigger(&self) { + tracing::trace!("{}::handle_read_change_trigger", self.human_readable_id()); if let Some(has_unread) = self.timeline().has_unread_messages().await { self.set_is_read(!has_unread); } diff --git a/src/session/model/room/timeline/mod.rs b/src/session/model/room/timeline/mod.rs index e9db2a67..860beab5 100644 --- a/src/session/model/room/timeline/mod.rs +++ b/src/session/model/room/timeline/mod.rs @@ -4,7 +4,12 @@ mod virtual_item; use std::{collections::HashMap, sync::Arc}; use futures_util::StreamExt; -use gtk::{gio, glib, glib::clone, prelude::*, subclass::prelude::*}; +use gtk::{ + gio, glib, + glib::{clone, closure_local}, + prelude::*, + subclass::prelude::*, +}; use matrix_sdk::Error as MatrixError; use matrix_sdk_ui::{ eyeball_im::VectorDiff, @@ -72,6 +77,9 @@ mod imp { marker::PhantomData, }; + use glib::subclass::Signal; + use once_cell::sync::Lazy; + use super::*; #[derive(Debug, glib::Properties)] @@ -98,14 +106,15 @@ mod imp { pub state: Cell, /// Whether this timeline has a typing row. pub has_typing: Cell, - pub diff_handle: OnceCell, - pub back_pagination_status_handle: OnceCell, /// Whether the timeline is empty. #[property(get = Self::is_empty)] pub empty: PhantomData, /// Whether the timeline has the `m.room.create` event of the room. #[property(get)] pub has_room_create: Cell, + pub diff_handle: OnceCell, + pub back_pagination_status_handle: OnceCell, + pub read_receipts_changed_handle: OnceCell, } impl Default for Timeline { @@ -129,10 +138,11 @@ mod imp { event_map: Default::default(), state: Default::default(), has_typing: Default::default(), - diff_handle: Default::default(), - back_pagination_status_handle: Default::default(), empty: Default::default(), has_room_create: Default::default(), + diff_handle: Default::default(), + back_pagination_status_handle: Default::default(), + read_receipts_changed_handle: Default::default(), } } } @@ -145,6 +155,12 @@ mod imp { #[glib::derived_properties] impl ObjectImpl for Timeline { + fn signals() -> &'static [Signal] { + static SIGNALS: Lazy> = + Lazy::new(|| vec![Signal::builder("read-change-trigger").build()]); + SIGNALS.as_ref() + } + fn dispose(&self) { if let Some(handle) = self.diff_handle.get() { handle.abort(); @@ -152,6 +168,9 @@ mod imp { if let Some(handle) = self.back_pagination_status_handle.get() { handle.abort(); } + if let Some(handle) = self.read_receipts_changed_handle.get() { + handle.abort(); + } } } @@ -366,6 +385,8 @@ impl Timeline { if self.empty() != was_empty { self.notify_empty(); } + + self.emit_read_change_trigger(); } /// Update `nb` items' headers starting at `pos`. @@ -633,6 +654,7 @@ impl Timeline { imp.diff_handle.set(diff_handle.abort_handle()).unwrap(); self.setup_back_pagination_status().await; + self.setup_read_receipts().await; } /// Setup the back-pagination status. @@ -675,6 +697,43 @@ impl Timeline { .unwrap(); } + /// Listen to read receipts changes. + async fn setup_read_receipts(&self) { + let Some(room) = self.room() else { + return; + }; + let room_id = room.room_id().to_owned(); + let matrix_timeline = self.matrix_timeline(); + + let stream = matrix_timeline + .subscribe_own_user_read_receipts_changed() + .await; + + let obj_weak = glib::SendWeakRef::from(self.downgrade()); + let fut = stream.for_each(move |_| { + let obj_weak = obj_weak.clone(); + let room_id = room_id.clone(); + async move { + let ctx = glib::MainContext::default(); + ctx.spawn(async move { + spawn!(async move { + if let Some(obj) = obj_weak.upgrade() { + obj.emit_read_change_trigger(); + } else { + error!("Could not emit read change trigger for room {room_id}: could not upgrade weak reference"); + } + }); + }); + } + }); + + let handle = spawn_tokio!(fut); + self.imp() + .read_receipts_changed_handle + .set(handle.abort_handle()) + .unwrap(); + } + /// The underlying SDK timeline. pub fn matrix_timeline(&self) -> Arc { self.imp().timeline.get().unwrap().clone() @@ -812,6 +871,25 @@ impl Timeline { events } + + /// Emit the trigger that a read change might have occurred. + fn emit_read_change_trigger(&self) { + self.emit_by_name::<()>("read-change-trigger", &[]); + } + + /// Connect to the trigger emitted when a read change might have occurred. + pub fn connect_read_change_trigger( + &self, + f: F, + ) -> glib::SignalHandlerId { + self.connect_closure( + "read-change-trigger", + true, + closure_local!(move |obj: Self| { + f(&obj); + }), + ) + } } /// Whether the given event is an `m.room.create` event.