Skip to content

Commit 6f0c641

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

File tree

3 files changed

+194
-99
lines changed

3 files changed

+194
-99
lines changed

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

Lines changed: 58 additions & 13 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::{
@@ -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
@@ -122,13 +124,15 @@ impl SpaceRoomList {
122124
&self,
123125
listener: Box<dyn SpaceRoomListEntriesListener>,
124126
) -> Arc<TaskHandle> {
125-
let entries_stream = self.inner.subscribe_to_room_updates();
127+
let (initial_values, mut stream) = self.inner.subscribe_to_room_updates();
126128

127-
Arc::new(TaskHandle::new(get_runtime_handle().spawn(async move {
128-
pin_mut!(entries_stream);
129+
listener.on_update(vec![SpaceListUpdate::Reset {
130+
values: initial_values.into_iter().map(Into::into).collect(),
131+
}]);
129132

130-
while let Some(rooms) = entries_stream.next().await {
131-
listener.on_update(rooms.into_iter().map(Into::into).collect());
133+
Arc::new(TaskHandle::new(get_runtime_handle().spawn(async move {
134+
while let Some(diffs) = stream.next().await {
135+
listener.on_update(diffs.into_iter().map(Into::into).collect());
132136
}
133137
})))
134138
}
@@ -145,12 +149,12 @@ pub trait SpaceRoomListPaginationStateListener: SendOutsideWasm + SyncOutsideWas
145149

146150
#[matrix_sdk_ffi_macros::export(callback_interface)]
147151
pub trait SpaceRoomListEntriesListener: SendOutsideWasm + SyncOutsideWasm + Debug {
148-
fn on_update(&self, rooms: Vec<SpaceRoom>);
152+
fn on_update(&self, rooms: Vec<SpaceListUpdate>);
149153
}
150154

151155
#[matrix_sdk_ffi_macros::export(callback_interface)]
152156
pub trait SpaceServiceJoinedSpacesListener: SendOutsideWasm + SyncOutsideWasm + Debug {
153-
fn on_update(&self, rooms: Vec<SpaceRoom>);
157+
fn on_update(&self, room_updates: Vec<SpaceListUpdate>);
154158
}
155159

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

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,11 @@
1616
//!
1717
//! See [`SpaceService`] for details.
1818
19-
use eyeball::{SharedObservable, Subscriber};
19+
use std::sync::Arc;
20+
21+
use eyeball_im::{ObservableVector, VectorSubscriberBatchedStream};
2022
use futures_util::pin_mut;
23+
use imbl::Vector;
2124
use matrix_sdk::{Client, deserialized_responses::SyncOrStrippedState, locks::Mutex};
2225
use matrix_sdk_common::executor::{JoinHandle, spawn};
2326
use ruma::{
@@ -39,7 +42,7 @@ pub mod room_list;
3942
pub struct SpaceService {
4043
client: Client,
4144

42-
joined_spaces: SharedObservable<Vec<SpaceRoom>>,
45+
joined_spaces: Arc<Mutex<ObservableVector<SpaceRoom>>>,
4346

4447
room_update_handle: Mutex<Option<JoinHandle<()>>>,
4548
}
@@ -56,15 +59,17 @@ impl SpaceService {
5659
pub fn new(client: Client) -> Self {
5760
Self {
5861
client,
59-
joined_spaces: SharedObservable::new(Vec::new()),
62+
joined_spaces: Arc::new(Mutex::new(ObservableVector::new())),
6063
room_update_handle: Mutex::new(None),
6164
}
6265
}
6366

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

7075
*self.room_update_handle.lock() = Some(spawn(async move {
@@ -73,10 +78,8 @@ impl SpaceService {
7378
loop {
7479
match all_room_updates_receiver.recv().await {
7580
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-
}
81+
let new_spaces = Vector::from(Self::joined_spaces_for(&client).await);
82+
Self::update_joined_spaces_if_needed(new_spaces, &joined_spaces);
8083
}
8184
Err(err) => {
8285
error!("error when listening to room updates: {err}");
@@ -86,15 +89,14 @@ impl SpaceService {
8689
}));
8790
}
8891

89-
self.joined_spaces.subscribe()
92+
self.joined_spaces.lock().subscribe().into_values_and_batched_stream()
9093
}
9194

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

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

99101
spaces
100102
}
@@ -103,6 +105,18 @@ impl SpaceService {
103105
SpaceRoomList::new(self.client.clone(), space_id)
104106
}
105107

108+
fn update_joined_spaces_if_needed(
109+
new_spaces: Vector<SpaceRoom>,
110+
joined_spaces: &Arc<Mutex<ObservableVector<SpaceRoom>>>,
111+
) {
112+
let old_spaces = joined_spaces.lock().clone();
113+
114+
if new_spaces != old_spaces {
115+
joined_spaces.lock().clear();
116+
joined_spaces.lock().append(Vector::from(new_spaces));
117+
}
118+
}
119+
106120
async fn joined_spaces_for(client: &Client) -> Vec<SpaceRoom> {
107121
let joined_spaces = client
108122
.joined_rooms()
@@ -175,6 +189,7 @@ impl SpaceService {
175189
#[cfg(test)]
176190
mod tests {
177191
use assert_matches2::assert_let;
192+
use eyeball_im::VectorDiff;
178193
use futures_util::pin_mut;
179194
use matrix_sdk::{room::ParentSpace, test_utils::mocks::MatrixMockServer};
180195
use matrix_sdk_test::{
@@ -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)