mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-05-18 13:40:55 -04:00
sdk-ui: add Timeline::pin_event and Timeline::unpin_event
This commit is contained in:
committed by
Andy Balaam
parent
15bf675e5e
commit
ab0494549e
@@ -44,7 +44,7 @@ pub(in crate::timeline) struct RemoteEventTimelineItem {
|
||||
/// Note that currently this ignores threads.
|
||||
pub read_receipts: IndexMap<OwnedUserId, Receipt>,
|
||||
|
||||
/// Whether the event has been sent by the the logged-in user themselves.
|
||||
/// Whether the event has been sent by the logged-in user themselves.
|
||||
pub is_own: bool,
|
||||
|
||||
/// Whether the item should be highlighted in the timeline.
|
||||
|
||||
@@ -170,7 +170,7 @@ impl TimelineInnerState {
|
||||
timestamp: MilliSecondsSinceUnixEpoch::now(),
|
||||
is_own_event: true,
|
||||
read_receipts: Default::default(),
|
||||
// An event sent by ourself is never matched against push rules.
|
||||
// An event sent by ourselves is never matched against push rules.
|
||||
is_highlighted: false,
|
||||
flow: Flow::Local { txn_id, send_handle },
|
||||
};
|
||||
|
||||
@@ -50,6 +50,7 @@ use ruma::{
|
||||
AddMentions, ForwardThread, OriginalRoomMessageEvent, RoomMessageEventContent,
|
||||
RoomMessageEventContentWithoutRelation,
|
||||
},
|
||||
pinned_events::RoomPinnedEventsEventContent,
|
||||
redaction::RoomRedactionEventContent,
|
||||
},
|
||||
AnyMessageLikeEventContent, AnySyncMessageLikeEvent, AnySyncTimelineEvent,
|
||||
@@ -857,6 +858,42 @@ impl Timeline {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a new pinned event by sending an updated `m.room.pinned_events`
|
||||
/// event containing the new event id.
|
||||
///
|
||||
/// Returns `true` if we sent the request, `false` if the event was already
|
||||
/// pinned.
|
||||
pub async fn pin_event(&self, event_id: &EventId) -> Result<bool> {
|
||||
let mut pinned_events = self.room().pinned_events();
|
||||
let event_id = event_id.to_owned();
|
||||
if pinned_events.contains(&event_id) {
|
||||
Ok(false)
|
||||
} else {
|
||||
pinned_events.push(event_id);
|
||||
let content = RoomPinnedEventsEventContent::new(pinned_events);
|
||||
self.room().send_state_event(content).await?;
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a new pinned event by sending an updated `m.room.pinned_events`
|
||||
/// event without the event id we want to remove.
|
||||
///
|
||||
/// Returns `true` if we sent the request, `false` if the event wasn't
|
||||
/// pinned.
|
||||
pub async fn unpin_event(&self, event_id: &EventId) -> Result<bool> {
|
||||
let mut pinned_events = self.room().pinned_events();
|
||||
let event_id = event_id.to_owned();
|
||||
if let Some(idx) = pinned_events.iter().position(|e| *e == *event_id) {
|
||||
pinned_events.remove(idx);
|
||||
let content = RoomPinnedEventsEventContent::new(pinned_events);
|
||||
self.room().send_state_event(content).await?;
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Test helpers, likely not very useful in production.
|
||||
|
||||
@@ -507,3 +507,169 @@ async fn test_duplicate_maintains_correct_order() {
|
||||
let content = items[3].as_event().unwrap().content().as_message().unwrap().body();
|
||||
assert_eq!(content, "C");
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_pin_event_is_sent_successfully() {
|
||||
let mut setup = PinningTestSetup::new().await;
|
||||
let timeline = setup.timeline().await;
|
||||
|
||||
setup.mock_sync(false).await;
|
||||
assert!(!timeline.items().await.is_empty());
|
||||
|
||||
// Pinning a remote event succeeds.
|
||||
setup
|
||||
.mock_response(ResponseTemplate::new(200).set_body_json(json!({
|
||||
"event_id": "$42"
|
||||
})))
|
||||
.await;
|
||||
|
||||
let event_id = setup.event_id();
|
||||
assert!(timeline.pin_event(event_id).await.unwrap());
|
||||
|
||||
setup.reset_server().await;
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_pin_event_is_returning_false_because_is_already_pinned() {
|
||||
let mut setup = PinningTestSetup::new().await;
|
||||
let timeline = setup.timeline().await;
|
||||
|
||||
setup.mock_sync(true).await;
|
||||
assert!(!timeline.items().await.is_empty());
|
||||
|
||||
let event_id = setup.event_id();
|
||||
assert!(!timeline.pin_event(event_id).await.unwrap());
|
||||
|
||||
setup.reset_server().await;
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_pin_event_is_returning_an_error() {
|
||||
let mut setup = PinningTestSetup::new().await;
|
||||
let timeline = setup.timeline().await;
|
||||
|
||||
setup.mock_sync(false).await;
|
||||
assert!(!timeline.items().await.is_empty());
|
||||
|
||||
// Pinning a remote event fails.
|
||||
setup.mock_response(ResponseTemplate::new(400)).await;
|
||||
|
||||
let event_id = setup.event_id();
|
||||
assert!(timeline.pin_event(event_id).await.is_err());
|
||||
|
||||
setup.reset_server().await;
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_unpin_event_is_sent_successfully() {
|
||||
let mut setup = PinningTestSetup::new().await;
|
||||
let timeline = setup.timeline().await;
|
||||
|
||||
setup.mock_sync(true).await;
|
||||
assert!(!timeline.items().await.is_empty());
|
||||
|
||||
// Unpinning a remote event succeeds.
|
||||
setup
|
||||
.mock_response(ResponseTemplate::new(200).set_body_json(json!({
|
||||
"event_id": "$42"
|
||||
})))
|
||||
.await;
|
||||
|
||||
let event_id = setup.event_id();
|
||||
assert!(timeline.unpin_event(event_id).await.unwrap());
|
||||
|
||||
setup.reset_server().await;
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_unpin_event_is_returning_false_because_is_not_pinned() {
|
||||
let mut setup = PinningTestSetup::new().await;
|
||||
let timeline = setup.timeline().await;
|
||||
|
||||
setup.mock_sync(false).await;
|
||||
assert!(!timeline.items().await.is_empty());
|
||||
|
||||
let event_id = setup.event_id();
|
||||
assert!(!timeline.unpin_event(event_id).await.unwrap());
|
||||
|
||||
setup.reset_server().await;
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_unpin_event_is_returning_an_error() {
|
||||
let mut setup = PinningTestSetup::new().await;
|
||||
let timeline = setup.timeline().await;
|
||||
|
||||
setup.mock_sync(true).await;
|
||||
assert!(!timeline.items().await.is_empty());
|
||||
|
||||
// Unpinning a remote event fails.
|
||||
setup.mock_response(ResponseTemplate::new(400)).await;
|
||||
|
||||
let event_id = setup.event_id();
|
||||
assert!(timeline.unpin_event(event_id).await.is_err());
|
||||
|
||||
setup.reset_server().await;
|
||||
}
|
||||
|
||||
struct PinningTestSetup<'a> {
|
||||
event_id: &'a ruma::EventId,
|
||||
room_id: &'a ruma::RoomId,
|
||||
client: matrix_sdk::Client,
|
||||
server: wiremock::MockServer,
|
||||
sync_settings: SyncSettings,
|
||||
sync_builder: SyncResponseBuilder,
|
||||
}
|
||||
|
||||
impl PinningTestSetup<'_> {
|
||||
async fn new() -> Self {
|
||||
let room_id = room_id!("!a98sd12bjh:example.org");
|
||||
let (client, server) = logged_in_client_with_server().await;
|
||||
let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000));
|
||||
|
||||
let mut sync_builder = SyncResponseBuilder::new();
|
||||
let event_id = event_id!("$a");
|
||||
sync_builder.add_joined_room(JoinedRoomBuilder::new(room_id));
|
||||
|
||||
mock_sync(&server, sync_builder.build_json_sync_response(), None).await;
|
||||
let _response = client.sync_once(sync_settings.clone()).await.unwrap();
|
||||
server.reset().await;
|
||||
|
||||
Self { event_id, room_id, client, server, sync_settings, sync_builder }
|
||||
}
|
||||
|
||||
async fn timeline(&self) -> matrix_sdk_ui::Timeline {
|
||||
let room = self.client.get_room(self.room_id).unwrap();
|
||||
room.timeline().await.unwrap()
|
||||
}
|
||||
|
||||
async fn reset_server(&self) {
|
||||
self.server.reset().await;
|
||||
}
|
||||
|
||||
async fn mock_response(&self, response: ResponseTemplate) {
|
||||
Mock::given(method("PUT"))
|
||||
.and(path_regex(r"^/_matrix/client/r0/rooms/.*/state/m.room.pinned_events/.*?"))
|
||||
.and(header("authorization", "Bearer 1234"))
|
||||
.respond_with(response)
|
||||
.mount(&self.server)
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn mock_sync(&mut self, is_using_pinned_state_event: bool) {
|
||||
let f = EventFactory::new().sender(user_id!("@a:b.c"));
|
||||
let mut joined_room_builder = JoinedRoomBuilder::new(self.room_id)
|
||||
.add_timeline_event(f.text_msg("A").event_id(self.event_id).into_raw_sync());
|
||||
if is_using_pinned_state_event {
|
||||
joined_room_builder =
|
||||
joined_room_builder.add_state_event(StateTestEvent::RoomPinnedEvents);
|
||||
}
|
||||
self.sync_builder.add_joined_room(joined_room_builder);
|
||||
mock_sync(&self.server, self.sync_builder.build_json_sync_response(), None).await;
|
||||
let _response = self.client.sync_once(self.sync_settings.clone()).await.unwrap();
|
||||
}
|
||||
|
||||
fn event_id(&self) -> &ruma::EventId {
|
||||
self.event_id
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ pub enum StateTestEvent {
|
||||
RedactedState,
|
||||
RoomAvatar,
|
||||
RoomName,
|
||||
RoomPinnedEvents,
|
||||
RoomTopic,
|
||||
Custom(JsonValue),
|
||||
}
|
||||
@@ -53,6 +54,7 @@ impl StateTestEvent {
|
||||
Self::RedactedState => test_json::sync_events::REDACTED_STATE.to_owned(),
|
||||
Self::RoomAvatar => test_json::sync_events::ROOM_AVATAR.to_owned(),
|
||||
Self::RoomName => test_json::sync_events::NAME.to_owned(),
|
||||
Self::RoomPinnedEvents => test_json::sync_events::PINNED_EVENTS.to_owned(),
|
||||
Self::RoomTopic => test_json::sync_events::TOPIC.to_owned(),
|
||||
Self::Custom(json) => json,
|
||||
}
|
||||
|
||||
@@ -317,6 +317,22 @@ pub static NAME_STRIPPED: Lazy<JsonValue> = Lazy::new(|| {
|
||||
})
|
||||
});
|
||||
|
||||
pub static PINNED_EVENTS: Lazy<JsonValue> = Lazy::new(|| {
|
||||
json!({
|
||||
"content": {
|
||||
"pinned": [ "$a" ]
|
||||
},
|
||||
"event_id": "$15139375513VdeRF:localhost",
|
||||
"origin_server_ts": 151393755,
|
||||
"sender": "@example:localhost",
|
||||
"state_key": "",
|
||||
"type": "m.room.pinned_events",
|
||||
"unsigned": {
|
||||
"age": 703422
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
pub static POWER_LEVELS: Lazy<JsonValue> = Lazy::new(|| {
|
||||
json!({
|
||||
"content": {
|
||||
|
||||
Reference in New Issue
Block a user