Skip to content

Commit cde1a3d

Browse files
committed
change(spaces): publish VectorDiffs instead of a full vectors for joined space subscriptions
1 parent 7b6cf7e commit cde1a3d

File tree

2 files changed

+118
-27
lines changed

2 files changed

+118
-27
lines changed

bindings/matrix-sdk-ffi/src/spaces.rs

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
use std::{fmt::Debug, sync::Arc};
1616

17+
use eyeball_im::VectorDiff;
1718
use futures_util::{pin_mut, StreamExt};
1819
use matrix_sdk_common::{SendOutsideWasm, SyncOutsideWasm};
1920
use matrix_sdk_ui::spaces::{
@@ -38,7 +39,7 @@ pub struct SpaceService {
3839

3940
impl SpaceService {
4041
pub(crate) fn new(inner: UISpaceService) -> Self {
41-
Self { inner }
42+
Self { inner: inner }
4243
}
4344
}
4445

@@ -57,17 +58,18 @@ impl SpaceService {
5758
&self,
5859
listener: Box<dyn SpaceServiceJoinedSpacesListener>,
5960
) -> Arc<TaskHandle> {
60-
let entries_stream = self.inner.subscribe_to_joined_spaces();
61+
let (initial_values, mut stream) = self.inner.subscribe_to_joined_spaces();
6162

62-
Arc::new(TaskHandle::new(get_runtime_handle().spawn(async move {
63-
pin_mut!(entries_stream);
63+
listener.on_update(vec![SpaceListUpdate::Reset {
64+
values: initial_values.into_iter().map(Into::into).collect(),
65+
}]);
6466

65-
while let Some(rooms) = entries_stream.next().await {
66-
listener.on_update(rooms.into_iter().map(Into::into).collect());
67+
Arc::new(TaskHandle::new(get_runtime_handle().spawn(async move {
68+
while let Some(diffs) = stream.next().await {
69+
listener.on_update(diffs.into_iter().map(Into::into).collect());
6770
}
6871
})))
6972
}
70-
7173
#[allow(clippy::unused_async)]
7274
// This method doesn't need to be async but if its not the FFI layer panics
7375
// with "there is no no reactor running, must be called from the context
@@ -150,7 +152,7 @@ pub trait SpaceRoomListEntriesListener: SendOutsideWasm + SyncOutsideWasm + Debu
150152

151153
#[matrix_sdk_ffi_macros::export(callback_interface)]
152154
pub trait SpaceServiceJoinedSpacesListener: SendOutsideWasm + SyncOutsideWasm + Debug {
153-
fn on_update(&self, rooms: Vec<SpaceRoom>);
155+
fn on_update(&self, room_updates: Vec<SpaceListUpdate>);
154156
}
155157

156158
#[derive(uniffi::Record)]
@@ -190,3 +192,44 @@ impl From<UISpaceRoom> for SpaceRoom {
190192
}
191193
}
192194
}
195+
196+
#[derive(uniffi::Enum)]
197+
pub enum SpaceListUpdate {
198+
Append { values: Vec<SpaceRoom> },
199+
Clear,
200+
PushFront { value: SpaceRoom },
201+
PushBack { value: SpaceRoom },
202+
PopFront,
203+
PopBack,
204+
Insert { index: u32, value: SpaceRoom },
205+
Set { index: u32, value: SpaceRoom },
206+
Remove { index: u32 },
207+
Truncate { length: u32 },
208+
Reset { values: Vec<SpaceRoom> },
209+
}
210+
211+
impl From<VectorDiff<UISpaceRoom>> for SpaceListUpdate {
212+
fn from(diff: VectorDiff<UISpaceRoom>) -> Self {
213+
match diff {
214+
VectorDiff::Append { values } => {
215+
Self::Append { values: values.into_iter().map(|v| v.into()).collect() }
216+
}
217+
VectorDiff::Clear => Self::Clear,
218+
VectorDiff::PushFront { value } => Self::PushFront { value: value.into() },
219+
VectorDiff::PushBack { value } => Self::PushBack { value: value.into() },
220+
VectorDiff::PopFront => Self::PopFront,
221+
VectorDiff::PopBack => Self::PopBack,
222+
VectorDiff::Insert { index, value } => {
223+
Self::Insert { index: index as u32, value: value.into() }
224+
}
225+
VectorDiff::Set { index, value } => {
226+
Self::Set { index: index as u32, value: value.into() }
227+
}
228+
VectorDiff::Remove { index } => Self::Remove { index: index as u32 },
229+
VectorDiff::Truncate { length } => Self::Truncate { length: length as u32 },
230+
VectorDiff::Reset { values } => {
231+
Self::Reset { values: values.into_iter().map(|v| v.into()).collect() }
232+
}
233+
}
234+
}
235+
}

crates/matrix-sdk-ui/src/spaces/mod.rs

Lines changed: 67 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,12 @@
1616
//!
1717
//! See [`SpaceService`] for details.
1818
19-
use eyeball::{SharedObservable, Subscriber};
19+
use std::sync::Arc;
20+
21+
use eyeball_im::{ObservableVector, VectorDiff, VectorSubscriber, VectorSubscriberBatchedStream};
22+
use futures_core::Stream;
2023
use futures_util::pin_mut;
24+
use imbl::Vector;
2125
use matrix_sdk::{Client, deserialized_responses::SyncOrStrippedState, locks::Mutex};
2226
use matrix_sdk_common::executor::{JoinHandle, spawn};
2327
use ruma::{
@@ -39,7 +43,7 @@ pub mod room_list;
3943
pub struct SpaceService {
4044
client: Client,
4145

42-
joined_spaces: SharedObservable<Vec<SpaceRoom>>,
46+
joined_spaces: Arc<Mutex<ObservableVector<SpaceRoom>>>,
4347

4448
room_update_handle: Mutex<Option<JoinHandle<()>>>,
4549
}
@@ -56,15 +60,17 @@ impl SpaceService {
5660
pub fn new(client: Client) -> Self {
5761
Self {
5862
client,
59-
joined_spaces: SharedObservable::new(Vec::new()),
63+
joined_spaces: Arc::new(Mutex::new(ObservableVector::new())),
6064
room_update_handle: Mutex::new(None),
6165
}
6266
}
6367

64-
pub fn subscribe_to_joined_spaces(&self) -> Subscriber<Vec<SpaceRoom>> {
68+
pub fn subscribe_to_joined_spaces(
69+
&self,
70+
) -> (Vector<SpaceRoom>, VectorSubscriberBatchedStream<SpaceRoom>) {
6571
if self.room_update_handle.lock().is_none() {
66-
let client_clone = self.client.clone();
67-
let joined_spaces_clone = self.joined_spaces.clone();
72+
let client = self.client.clone();
73+
let joined_spaces = Arc::clone(&self.joined_spaces);
6874
let all_room_updates_receiver = self.client.subscribe_to_all_room_updates();
6975

7076
*self.room_update_handle.lock() = Some(spawn(async move {
@@ -73,10 +79,8 @@ impl SpaceService {
7379
loop {
7480
match all_room_updates_receiver.recv().await {
7581
Ok(_) => {
76-
let new_spaces = Self::joined_spaces_for(&client_clone).await;
77-
if new_spaces != joined_spaces_clone.get() {
78-
joined_spaces_clone.set(new_spaces);
79-
}
82+
let new_spaces = Vector::from(Self::joined_spaces_for(&client).await);
83+
Self::update_joined_spaces_if_needed(new_spaces, &joined_spaces);
8084
}
8185
Err(err) => {
8286
error!("error when listening to room updates: {err}");
@@ -86,15 +90,14 @@ impl SpaceService {
8690
}));
8791
}
8892

89-
self.joined_spaces.subscribe()
93+
self.joined_spaces.lock().subscribe().into_values_and_batched_stream()
9094
}
9195

9296
pub async fn joined_spaces(&self) -> Vec<SpaceRoom> {
9397
let spaces = Self::joined_spaces_for(&self.client).await;
9498

95-
if spaces != self.joined_spaces.get() {
96-
self.joined_spaces.set(spaces.clone());
97-
}
99+
self.joined_spaces.lock().clear();
100+
self.joined_spaces.lock().append(Vector::from(spaces.clone()));
98101

99102
spaces
100103
}
@@ -103,6 +106,18 @@ impl SpaceService {
103106
SpaceRoomList::new(self.client.clone(), space_id)
104107
}
105108

109+
fn update_joined_spaces_if_needed(
110+
new_spaces: Vector<SpaceRoom>,
111+
joined_spaces: &Arc<Mutex<ObservableVector<SpaceRoom>>>,
112+
) {
113+
let old_spaces = joined_spaces.lock().clone();
114+
115+
if new_spaces != old_spaces {
116+
joined_spaces.lock().clear();
117+
joined_spaces.lock().append(Vector::from(new_spaces));
118+
}
119+
}
120+
106121
async fn joined_spaces_for(client: &Client) -> Vec<SpaceRoom> {
107122
let joined_spaces = client
108123
.joined_rooms()
@@ -340,7 +355,7 @@ mod tests {
340355

341356
let space_service = SpaceService::new(client.clone());
342357

343-
let joined_spaces_subscriber = space_service.subscribe_to_joined_spaces();
358+
let (_, joined_spaces_subscriber) = space_service.subscribe_to_joined_spaces();
344359
pin_mut!(joined_spaces_subscriber);
345360
assert_pending!(joined_spaces_subscriber);
346361

@@ -349,6 +364,17 @@ mod tests {
349364
vec![SpaceRoom::new_from_known(client.get_room(first_space_id).unwrap(), 0)]
350365
);
351366

367+
assert_next_eq!(
368+
joined_spaces_subscriber,
369+
vec![VectorDiff::Append {
370+
values: vec![SpaceRoom::new_from_known(
371+
client.get_room(first_space_id).unwrap(),
372+
0
373+
)]
374+
.into()
375+
}]
376+
);
377+
352378
// Join the second space
353379

354380
server
@@ -380,12 +406,25 @@ mod tests {
380406
]
381407
);
382408

383-
// The subscriber yields new results when a space is joined
384409
assert_next_eq!(
385410
joined_spaces_subscriber,
386411
vec![
387-
SpaceRoom::new_from_known(client.get_room(first_space_id).unwrap(), 0),
388-
SpaceRoom::new_from_known(client.get_room(second_space_id).unwrap(), 1)
412+
VectorDiff::Clear,
413+
VectorDiff::Append {
414+
values: vec![
415+
SpaceRoom::new_from_known(client.get_room(first_space_id).unwrap(), 0),
416+
SpaceRoom::new_from_known(client.get_room(second_space_id).unwrap(), 1)
417+
]
418+
.into()
419+
},
420+
VectorDiff::Clear,
421+
VectorDiff::Append {
422+
values: vec![
423+
SpaceRoom::new_from_known(client.get_room(first_space_id).unwrap(), 0),
424+
SpaceRoom::new_from_known(client.get_room(second_space_id).unwrap(), 1)
425+
]
426+
.into()
427+
}
389428
]
390429
);
391430

@@ -394,7 +433,16 @@ mod tests {
394433
// and when one is left
395434
assert_next_eq!(
396435
joined_spaces_subscriber,
397-
vec![SpaceRoom::new_from_known(client.get_room(first_space_id).unwrap(), 0)]
436+
vec![
437+
VectorDiff::Clear,
438+
VectorDiff::Append {
439+
values: vec![SpaceRoom::new_from_known(
440+
client.get_room(first_space_id).unwrap(),
441+
0
442+
)]
443+
.into()
444+
},
445+
]
398446
);
399447

400448
// but it doesn't when a non-space room gets joined

0 commit comments

Comments
 (0)