Merge pull request #3540 from Hywan/fix-ui-roomlist-latest-event

chore(ui): `room_list_service::Room::latest_event` no longer uses `SlidingSyncRoom` (+ drop `SlidingSyncRoomExt`)
This commit is contained in:
Ivan Enderlin
2024-06-13 14:08:27 +02:00
committed by GitHub
4 changed files with 22 additions and 156 deletions

View File

@@ -22,7 +22,7 @@ use ruma::{api::client::sync::sync_events::v4::RoomSubscription, events::StateEv
use super::Error;
use crate::{
timeline::{EventTimelineItem, SlidingSyncRoomExt, TimelineBuilder},
timeline::{EventTimelineItem, TimelineBuilder},
Timeline,
};
@@ -150,10 +150,15 @@ impl Room {
/// Get the latest event in the timeline.
///
/// The latest event comes first from the `Timeline` if it exists and if it
/// contains a local event. Otherwise, it comes from the cache. This method
/// does not fetch any events or calculate anything — if it's not
/// already available, we return `None`.
/// The latest event comes first from the `Timeline`, it can be a local or a
/// remote event. Note that the `Timeline` can have more information esp. if
/// it has run a backpagination for example. Otherwise if the `Timeline`
/// doesn't have any latest event, it comes from the cache. This method
/// does not fetch any events or calculate anything — if it's not already
/// available, we return `None`.
///
/// Reminder: this method also returns `None` is the latest event is not
/// suitable for use in a message preview.
pub async fn latest_event(&self) -> Option<EventTimelineItem> {
// Look for a local event in the `Timeline`.
//
@@ -161,15 +166,22 @@ impl Room {
if let Some(timeline) = self.inner.timeline.get() {
// If it contains a `latest_event`…
if let Some(timeline_last_event) = timeline.latest_event().await {
// If it's a local echo…
if timeline_last_event.is_local_echo() {
return Some(timeline_last_event);
}
// If it's a local event or a remote event, we return it.
return Some(timeline_last_event);
}
}
// Otherwise, fallback to the classical path.
self.inner.sliding_sync_room.latest_timeline_item().await
if let Some(latest_event) = self.inner.room.latest_event() {
EventTimelineItem::from_latest_event(
self.inner.room.client(),
self.inner.room.room_id(),
latest_event,
)
.await
} else {
None
}
}
/// Create a new [`TimelineBuilder`] with the default configuration.

View File

@@ -78,7 +78,6 @@ mod pagination;
mod polls;
mod reactions;
mod read_receipts;
mod sliding_sync_ext;
#[cfg(test)]
mod tests;
#[cfg(feature = "e2e-encryption")]
@@ -102,7 +101,6 @@ pub use self::{
pagination::LiveBackPaginationStatus,
polls::PollResult,
reactions::ReactionSenderData,
sliding_sync_ext::SlidingSyncRoomExt,
traits::RoomExt,
virtual_item::VirtualTimelineItem,
};

View File

@@ -1,140 +0,0 @@
// Copyright 2023 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use async_trait::async_trait;
use matrix_sdk::SlidingSyncRoom;
use tracing::instrument;
use super::EventTimelineItem;
#[async_trait]
pub trait SlidingSyncRoomExt {
/// Get the latest timeline item of this room, if it is already cached.
///
/// Use `Timeline::latest_event` instead if you already have a timeline for
/// this `SlidingSyncRoom`.
async fn latest_timeline_item(&self) -> Option<EventTimelineItem>;
}
#[async_trait]
impl SlidingSyncRoomExt for SlidingSyncRoom {
/// Get a timeline item representing the latest event in this room.
/// This method wraps latest_event, converting the event into an
/// EventTimelineItem.
#[instrument(skip_all)]
async fn latest_timeline_item(&self) -> Option<EventTimelineItem> {
let latest_event =
self.client().get_room(self.room_id()).and_then(|room| room.latest_event())?;
EventTimelineItem::from_latest_event(self.client(), self.room_id(), latest_event).await
}
}
#[cfg(test)]
mod tests {
use assert_matches::assert_matches;
use matrix_sdk::{test_utils::logged_in_client, Client, SlidingSyncRoom};
use matrix_sdk_base::deserialized_responses::SyncTimelineEvent;
use matrix_sdk_test::async_test;
use ruma::{
api::client::sync::sync_events::v4,
events::room::message::{MessageFormat, MessageType},
room_id,
serde::Raw,
user_id, RoomId, UInt, UserId,
};
use serde_json::json;
use crate::timeline::{SlidingSyncRoomExt, TimelineDetails};
#[async_test]
async fn test_initially_latest_message_event_is_none() {
// Given a room with no latest event
let room_id = room_id!("!r:x.uk").to_owned();
let client = logged_in_client(None).await;
let room = SlidingSyncRoom::new(client, room_id, None, Vec::new());
// When we ask for the latest event, it is None
assert!(room.latest_timeline_item().await.is_none());
}
#[async_test]
async fn test_latest_message_event_is_wrapped_as_a_timeline_item() {
// Given a room exists, and an event came in through a sync
let room_id = room_id!("!r:x.uk");
let user_id = user_id!("@s:o.uk");
let client = logged_in_client(None).await;
let event = message_event(room_id, user_id, "**My msg**", "<b>My msg</b>", 122343);
process_event_via_sync_test_helper(room_id, event, &client).await;
// When we ask for the latest event in the room
let room = SlidingSyncRoom::new(client.clone(), room_id.to_owned(), None, Vec::new());
let actual = room.latest_timeline_item().await.unwrap();
// Then it is wrapped as an EventTimelineItem
assert_eq!(actual.sender, user_id);
assert_matches!(actual.sender_profile, TimelineDetails::Unavailable);
assert_eq!(actual.timestamp.0, UInt::new(122343).unwrap());
if let MessageType::Text(txt) = actual.content.as_message().unwrap().msgtype() {
assert_eq!(txt.body, "**My msg**");
let formatted = txt.formatted.as_ref().unwrap();
assert_eq!(formatted.format, MessageFormat::Html);
assert_eq!(formatted.body, "<b>My msg</b>");
} else {
panic!("Unexpected message type");
}
}
async fn process_event_via_sync_test_helper(
room_id: &RoomId,
event: SyncTimelineEvent,
client: &Client,
) {
let mut room = v4::SlidingSyncRoom::new();
room.timeline.push(event.event);
let mut response = v4::Response::new("6".to_owned());
response.rooms.insert(room_id.to_owned(), room);
client.process_sliding_sync_test_helper(&response).await.unwrap();
}
fn message_event(
room_id: &RoomId,
user_id: &UserId,
body: &str,
formatted_body: &str,
ts: u64,
) -> SyncTimelineEvent {
SyncTimelineEvent::new(
Raw::from_json_string(
json!({
"event_id": "$eventid6",
"sender": user_id,
"origin_server_ts": ts,
"type": "m.room.message",
"room_id": room_id.to_string(),
"content": {
"body": body,
"format": "org.matrix.custom.html",
"formatted_body": formatted_body,
"msgtype": "m.text"
},
})
.to_string(),
)
.unwrap(),
)
}
}

View File

@@ -343,10 +343,6 @@ copies accordingly. Because of where the loop sits in the stack, that can
be a bit tedious though, so lists and rooms have an additional way of
subscribing to updates via [`eyeball`].
The `Timeline` one can receive per room by calling `.timeline()` (from
`matrix_sdk_ui::timeline::SlidingSyncRoomExt`) will be populated with the
currently cached timeline events.
## Caching
All room data, for filled but also _invalidated_ rooms, including the entire