[fractal/fractal-next] sidebar: Order rooms by latest message
- From: Julian Sparber <jsparber src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [fractal/fractal-next] sidebar: Order rooms by latest message
- Date: Mon, 28 Mar 2022 14:57:15 +0000 (UTC)
commit a2a7800c1bac6d71ef2269ce9b16b5d81c95f915
Author: Kévin Commaille <zecakeh tedomum fr>
Date: Sun Mar 27 16:26:17 2022 +0200
sidebar: Order rooms by latest message
Only use latest messages or the user join state event.
Bind to property so the order is updated when it changes.
Fixes #858
src/session/room/event.rs | 43 +++++++++++++++++++++++++++++++++++++++--
src/session/room/mod.rs | 38 ++++++++++++++++++++----------------
src/session/room/timeline.rs | 41 +++++++++++++++++++++++++++++++++------
src/session/sidebar/category.rs | 9 ++++-----
4 files changed, 101 insertions(+), 30 deletions(-)
---
diff --git a/src/session/room/event.rs b/src/session/room/event.rs
index 1c1e6c3df..1b8b1ab3e 100644
--- a/src/session/room/event.rs
+++ b/src/session/room/event.rs
@@ -12,7 +12,10 @@ use matrix_sdk::{
media::MediaEventContent,
ruma::{
events::{
- room::message::{MessageType, Relation},
+ room::{
+ member::MembershipState,
+ message::{MessageType, Relation},
+ },
AnyMessageEventContent, AnySyncMessageEvent, AnySyncRoomEvent, AnySyncStateEvent,
Unsigned,
},
@@ -25,7 +28,7 @@ use matrix_sdk::{
use crate::{
session::{
room::{Member, ReactionList},
- Room,
+ Room, UserExt,
},
spawn_tokio,
utils::{filename_for_mime, media_type_uid},
@@ -268,6 +271,24 @@ impl Event {
.and_then(|unsigned| unsigned.transaction_id)
}
+ /// The original timestamp of this event.
+ pub fn matrix_origin_server_ts(&self) -> MilliSecondsSinceUnixEpoch {
+ let priv_ = self.imp();
+ if let Some(event) = priv_.event.borrow().as_ref() {
+ event.origin_server_ts().to_owned()
+ } else {
+ priv_
+ .pure_event
+ .borrow()
+ .as_ref()
+ .unwrap()
+ .event
+ .get_field::<MilliSecondsSinceUnixEpoch>("origin_server_ts")
+ .unwrap()
+ .unwrap()
+ }
+ }
+
/// The pretty-formatted JSON of this matrix event.
pub fn original_source(&self) -> String {
// We have to convert it to a Value, because a RawValue cannot be
@@ -721,4 +742,22 @@ impl Event {
.await?;
Ok(Some(event))
}
+
+ /// Whether this `Event` can be used as the `latest_change` of a room.
+ ///
+ /// This means that the event is a message, or it is the state event of the
+ /// user joining the room, which should be the oldest possible change.
+ pub fn can_be_latest_change(&self) -> bool {
+ if let Some(event) = self.matrix_event() {
+ matches!(event, AnySyncRoomEvent::Message(_))
+ || matches!(event, AnySyncRoomEvent::State(AnySyncStateEvent::RoomMember(event))
+ if event.state_key == self.room().session().user().unwrap().user_id().to_string()
+ && event.content.membership == MembershipState::Join
+ && event.prev_content.as_ref()
+ .filter(|content| content.membership == MembershipState::Join).is_none()
+ )
+ } else {
+ false
+ }
+ }
}
diff --git a/src/session/room/mod.rs b/src/session/room/mod.rs
index 3c8d0bb0d..3bd95d9ab 100644
--- a/src/session/room/mod.rs
+++ b/src/session/room/mod.rs
@@ -86,7 +86,7 @@ mod imp {
pub inviter: RefCell<Option<Member>>,
pub members_loaded: Cell<bool>,
pub power_levels: RefCell<PowerLevels>,
- pub latest_change: RefCell<Option<glib::DateTime>>,
+ pub latest_change: Cell<u64>,
pub predecessor: OnceCell<Box<RoomId>>,
pub successor: OnceCell<Box<RoomId>>,
}
@@ -176,11 +176,13 @@ mod imp {
None,
glib::ParamFlags::READWRITE,
),
- glib::ParamSpecBoxed::new(
+ glib::ParamSpecUInt64::new(
"latest-change",
"Latest Change",
- "Latest origin_server_ts of all loaded invents",
- glib::DateTime::static_type(),
+ "Timestamp of the latest message",
+ u64::MIN,
+ u64::MAX,
+ u64::default(),
glib::ParamFlags::READABLE,
),
glib::ParamSpecObject::new(
@@ -797,12 +799,9 @@ impl Room {
/// Update the room state based on the new sync response
/// FIXME: We should use the sdk's event handler to get updates
pub fn update_for_events(&self, batch: Vec<SyncRoomEvent>) {
- let priv_ = self.imp();
-
// FIXME: notify only when the count has changed
self.notify_notification_count();
- let mut latest_change = self.latest_change();
for event in batch.iter().flat_map(|e| e.event.deserialize().ok()) {
match &event {
AnySyncRoomEvent::State(AnySyncStateEvent::RoomMember(event)) => {
@@ -826,22 +825,27 @@ impl Room {
}
_ => {}
}
- let event_is_join_or_leave = matches!(&event,
AnySyncRoomEvent::State(AnySyncStateEvent::RoomMember(event))
- if event.content.membership == MembershipState::Join || event.content.membership ==
MembershipState::Leave);
- if !event_is_join_or_leave {
- let event_ts = glib::DateTime::from_unix_millis_utc(event.origin_server_ts());
- latest_change = latest_change.max(event_ts.ok());
- }
}
- priv_.latest_change.replace(latest_change);
self.notify("latest-change");
self.emit_by_name::<()>("order-changed", &[]);
}
- /// Returns the point in time this room received its latest event.
- pub fn latest_change(&self) -> Option<glib::DateTime> {
- self.imp().latest_change.borrow().clone()
+ /// The timestamp of the room's latest message.
+ ///
+ /// If it is not known, it will return 0.
+ pub fn latest_change(&self) -> u64 {
+ self.imp().latest_change.get()
+ }
+
+ /// Set the timestamp of the room's latest message.
+ pub fn set_latest_change(&self, latest_change: u64) {
+ if latest_change == self.latest_change() {
+ return;
+ }
+
+ self.imp().latest_change.set(latest_change);
+ self.notify("latest-change");
}
pub fn load_members(&self) {
diff --git a/src/session/room/timeline.rs b/src/session/room/timeline.rs
index 0f4f322c1..afbdee032 100644
--- a/src/session/room/timeline.rs
+++ b/src/session/room/timeline.rs
@@ -576,6 +576,21 @@ impl Timeline {
self.set_state(TimelineState::Error);
return false;
}
+
+ // Update the latest change of the room.
+ let room = self.room();
+ let mut latest_change = room.latest_change();
+ // We receive the events in reverse chronological order so start from the
+ // beginning.
+ for event in events.iter() {
+ if event.can_be_latest_change() {
+ latest_change =
+ latest_change.max(event.matrix_origin_server_ts().get().into());
+ break;
+ }
+ }
+ room.set_latest_change(latest_change);
+
self.set_state(TimelineState::Ready);
self.prepend(events);
true
@@ -944,12 +959,26 @@ async fn handle_forward_stream(
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(),
- );
+ let events: Vec<_> = events
+ .into_iter()
+ .map(|event| Event::new(event, &timeline.room()))
+ .collect();
+
+ // Update the latest change of the room.
+ let room = timeline.room();
+ let mut latest_change = room.latest_change();
+ // We receive the events in chronological order so start from the end.
+ let mut iter = events.iter();
+ while let Some(event) = iter.next_back() {
+ if event.can_be_latest_change() {
+ latest_change =
+ latest_change.max(event.matrix_origin_server_ts().get().into());
+ break;
+ }
+ }
+ room.set_latest_change(latest_change);
+
+ timeline.append(events);
true
} else {
diff --git a/src/session/sidebar/category.rs b/src/session/sidebar/category.rs
index 9483c0ca8..ba8d25c49 100644
--- a/src/session/sidebar/category.rs
+++ b/src/session/sidebar/category.rs
@@ -137,11 +137,10 @@ impl Category {
});
let filter_model = gtk::FilterListModel::new(Some(&model), Some(&filter));
- let sorter = gtk::CustomSorter::new(|a, b| {
- let a = a.downcast_ref::<Room>().unwrap();
- let b = b.downcast_ref::<Room>().unwrap();
- b.latest_change().cmp(&a.latest_change()).into()
- });
+ let sorter = gtk::NumericSorter::builder()
+ .expression(Room::this_expression("latest-change"))
+ .sort_order(gtk::SortType::Descending)
+ .build();
let sort_model = gtk::SortListModel::new(Some(&filter_model), Some(&sorter));
sort_model.upcast()
} else {
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]