Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions src/home/loading_pane.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,12 @@ impl Drop for LoadingPane {
fn drop(&mut self) {
if let LoadingPaneState::BackwardsPaginateUntilEvent { target_event_id, request_sender, .. } = &self.state {
warning!("Dropping LoadingPane with target_event_id: {}", target_event_id);
request_sender.send_if_modified(|requests| {
let initial_len = requests.len();
requests.retain(|r| &r.target_event_id != target_event_id);
request_sender.send_if_modified(|req| {
let initial_len = req.backwards_paginate.len();
req.backwards_paginate.retain(|r| &r.target_event_id != target_event_id);
// if we actually cancelled this request, notify the receivers
// such that they can stop looking for the target event.
requests.len() != initial_len
req.backwards_paginate.len() != initial_len
});
}
}
Expand Down Expand Up @@ -176,12 +176,12 @@ impl Widget for LoadingPane {
};
if close_pane {
if let LoadingPaneState::BackwardsPaginateUntilEvent { target_event_id, request_sender, .. } = &self.state {
let _did_send = request_sender.send_if_modified(|requests| {
let initial_len = requests.len();
requests.retain(|r| &r.target_event_id != target_event_id);
let _did_send = request_sender.send_if_modified(|req| {
let initial_len = req.backwards_paginate.len();
req.backwards_paginate.retain(|r| &r.target_event_id != target_event_id);
// if we actually cancelled this request, notify the receivers
// such that they can stop looking for the target event.
requests.len() != initial_len
req.backwards_paginate.len() != initial_len
});
log!("LoadingPane: {} cancel request for target_event_id: {target_event_id}",
if _did_send { "Sent" } else { "Did not send" },
Expand Down
20 changes: 15 additions & 5 deletions src/home/room_screen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1616,8 +1616,8 @@ impl RoomScreen {
}
TimelineUpdate::TargetEventFound { target_event_id, index } => {
// log!("Target event found in room {}: {target_event_id}, index: {index}", tl.kind.room_id());
tl.request_sender.send_if_modified(|requests| {
requests.retain(|r| &r.room_id != tl.kind.room_id());
tl.request_sender.send_if_modified(|req| {
req.backwards_paginate.retain(|r| &r.room_id != tl.kind.room_id());
// no need to notify/wake-up all receivers for a completed request
false
});
Expand Down Expand Up @@ -2478,13 +2478,13 @@ impl RoomScreen {
);
loading_pane.show(cx);

tl.request_sender.send_if_modified(|requests| {
if let Some(existing) = requests.iter_mut().find(|r| &r.room_id == tl.kind.room_id()) {
tl.request_sender.send_if_modified(|req| {
if let Some(existing) = req.backwards_paginate.iter_mut().find(|r| &r.room_id == tl.kind.room_id()) {
warning!("Unexpected: room {} already had an existing timeline request in progress, event: {:?}", tl.kind.room_id(), existing.target_event_id);
// We might as well re-use this existing request...
existing.target_event_id = target_event_id.clone();
} else {
requests.push(BackwardsPaginateUntilEventRequest {
req.backwards_paginate.push(BackwardsPaginateUntilEventRequest {
room_id: tl.kind.room_id().clone(),
target_event_id: target_event_id.clone(),
// avoid re-searching through items we already searched through.
Expand Down Expand Up @@ -2685,6 +2685,11 @@ impl RoomScreen {
// such that it can be accessed in future functions like event/draw handlers.
self.tl_state = Some(tl_state);

// Tell the background subscriber that this timeline is now open (if it was previously closed).
if let Some(tl) = self.tl_state.as_ref() {
tl.request_sender.send_if_modified(|req| !std::mem::replace(&mut req.is_timeline_open, true));
}

// Now that we have restored the TimelineUiState into this RoomScreen widget,
// we can proceed to processing pending background updates.
self.process_timeline_updates(cx, &self.portal_list(cx, ids!(list)));
Expand All @@ -2699,6 +2704,11 @@ impl RoomScreen {
return;
}

// Tell the background subscriber that this timeline is now closed.
if let Some(tl) = self.tl_state.as_ref() {
tl.request_sender.send_if_modified(|req| std::mem::replace(&mut req.is_timeline_open, false));
}

self.save_state();

// When closing a room view, we do the following with non-persistent states.
Expand Down
3 changes: 1 addition & 2 deletions src/home/rooms_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1498,8 +1498,7 @@ impl Widget for RoomsList {
scope = Scope::with_props(&*direct_room);
item.draw_all(cx, &mut scope);
} else {
list.item(cx, portal_list_index, id!(empty))
.draw_all(cx, &mut scope);
list.item(cx, portal_list_index, id!(empty)).draw_all(cx, &mut scope);
}
}
else if self.regular_rooms_indexes.header_index == Some(portal_list_index) {
Expand Down
117 changes: 83 additions & 34 deletions src/sliding_sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1010,7 +1010,10 @@ async fn matrix_worker_task(
log!("Successfully created thread-focused timeline for room {room_id}, thread {thread_root_event_id}.");
let thread_timeline = Arc::new(thread_timeline);
let (timeline_update_sender, timeline_update_receiver) = crossbeam_channel::unbounded();
let (request_sender, request_receiver) = watch::channel(Vec::new());
let (request_sender, request_receiver) = watch::channel(TimelineRequest {
backwards_paginate: Vec::new(),
is_timeline_open: true,
});
let timeline_subscriber_handler_task = Handle::current().spawn(
timeline_subscriber_handler(
thread_timeline.clone(),
Expand Down Expand Up @@ -2410,7 +2413,18 @@ pub fn start_matrix_tokio() -> Result<tokio::runtime::Handle> {

/// A tokio::watch channel sender for sending requests from the RoomScreen UI widget
/// to the corresponding background async task for that room (its `timeline_subscriber_handler`).
pub type TimelineRequestSender = watch::Sender<Vec<BackwardsPaginateUntilEventRequest>>;
pub type TimelineRequestSender = watch::Sender<TimelineRequest>;

/// Details of current requests that the RoomScreen UI has made of the background timeline subscriber task.
pub struct TimelineRequest {
/// Pending backwards-pagination-until-event requests (jump to a specific event).
pub backwards_paginate: Vec<BackwardsPaginateUntilEventRequest>,
/// Whether this timeline is currently open in the UI.
///
/// The timeline subscriber stops sending updates while it's closed,
/// and when it gets re-opened, it sends one catch-up update.
pub is_timeline_open: bool,
}

/// The return type for [`take_timeline_endpoints()`].
///
Expand All @@ -2431,7 +2445,7 @@ pub struct TimelineEndpoints {
enum TimelineSubscriber {
/// The timeline (room or thread) hasn't been opened yet, so its background subscriber task isn't running.
NotStarted {
request_receiver: watch::Receiver<Vec<BackwardsPaginateUntilEventRequest>>,
request_receiver: watch::Receiver<TimelineRequest>,
},
/// The timeline's background subscriber task is running, meaning the room has been opened at least once.
Running(JoinHandle<()>),
Expand Down Expand Up @@ -3734,7 +3748,10 @@ async fn add_new_room(

// The `timeline_subscriber_handler` async task is spawned lazily when the room/thread is first opened.
// All we do here is set up a channel between the UI and backend, for future use.
let (request_sender, request_receiver) = watch::channel(Vec::new());
let (request_sender, request_receiver) = watch::channel(TimelineRequest {
backwards_paginate: Vec::new(),
is_timeline_open: true,
});

// We need to add the room to the `ALL_JOINED_ROOMS` list before we can send
// an `AddJoinedRoom` update to the RoomsList widget, because that widget might
Expand Down Expand Up @@ -4375,7 +4392,7 @@ const LOG_ROOM_LIST_DIFFS: bool = cfg!(feature = "log_room_list_diffs");
async fn timeline_subscriber_handler(
timeline: Arc<Timeline>,
timeline_update_sender: crossbeam_channel::Sender<TimelineUpdate>,
mut request_receiver: watch::Receiver<Vec<BackwardsPaginateUntilEventRequest>>,
mut request_receiver: watch::Receiver<TimelineRequest>,
thread_root_event_id: Option<OwnedEventId>,
) {
/// An inner function that searches the given new timeline items for a target event.
Expand Down Expand Up @@ -4419,6 +4436,13 @@ async fn timeline_subscriber_handler(
// the timeline index and event ID of the target event, if it has been found.
let mut found_target_event_id: Option<(usize, OwnedEventId)> = None;

// Whether this timeline is currently open in the UI.
// This starts as true because we only spawn this subscriber task lazily upon open.
let mut is_timeline_open = true;
// Whether any update changes have arrived since this timeline was last closed,
// meaning that we need to send an cumulative update when the timeline gets re-opened.
let mut has_unsent_changes = false;

loop { tokio::select! {
// we should check for new requests before handling new timeline updates,
// because the request might influence how we handle a timeline update.
Expand All @@ -4427,13 +4451,31 @@ async fn timeline_subscriber_handler(
// Handle updates to the current backwards pagination requests.
Ok(()) = request_receiver.changed() => {
let prev_target_event_id = target_event_id.clone();
let new_request_details = request_receiver
.borrow_and_update()
.iter()
.find_map(|req| req.room_id
.eq(&room_id)
.then(|| (req.target_event_id.clone(), req.starting_index, req.current_tl_len))
);
let (now_open, new_request_details) = {
let req = request_receiver.borrow_and_update();
let details = req.backwards_paginate.iter()
.find_map(|r| r.room_id
.eq(&room_id)
.then(|| (r.target_event_id.clone(), r.starting_index, r.current_tl_len))
);
(req.is_timeline_open, details)
};

// On reopen, send one catch-up snapshot, but only if something actually
// changed while closed (otherwise the UI already has the current items).
if now_open && !is_timeline_open && has_unsent_changes {
let len = timeline_items.len();
if timeline_update_sender.send(TimelineUpdate::NewItems {
new_items: timeline_items.clone(),
changed_indices: 0..len,
clear_cache: true,
is_append: false,
}).is_ok() {
SignalToUI::set_ui_signal();
}
has_unsent_changes = false;
}
is_timeline_open = now_open;

target_event_id = new_request_details.as_ref().map(|(ev, ..)| ev.clone());

Expand Down Expand Up @@ -4643,29 +4685,36 @@ async fn timeline_subscriber_handler(
if LOG_TIMELINE_DIFFS {
log!("timeline_subscriber: applied {num_updates} updates for room {room_id}, thread {thread_root_event_id:?}, timeline now has {} items. is_append? {is_append}, clear_cache? {clear_cache}. Changes: {changed_indices:?}.", timeline_items.len());
}
timeline_update_sender.send(TimelineUpdate::NewItems {
new_items: timeline_items.clone(),
changed_indices,
clear_cache,
is_append,
}).expect("Error: timeline update sender couldn't send update with new items!");

// We must send this update *after* the actual NewItems update,
// otherwise the UI thread (RoomScreen) won't be able to correctly locate the target event.
if let Some((index, found_event_id)) = found_target_event_id.take() {
target_event_id = None;
timeline_update_sender.send(
TimelineUpdate::TargetEventFound {
target_event_id: found_event_id.clone(),
index,
}
).unwrap_or_else(
|_e| panic!("Error: timeline update sender couldn't send TargetEventFound({found_event_id}, {index}) to room {room_id}, thread {thread_root_event_id:?}!")
);
}
// Only send updates to the UI while this timeline is open.
// While it's closed, we process the updates locally until it is re-opened again.
if is_timeline_open {
timeline_update_sender.send(TimelineUpdate::NewItems {
new_items: timeline_items.clone(),
changed_indices,
clear_cache,
is_append,
}).expect("Error: timeline update sender couldn't send update with new items!");

// We must send this update *after* the actual NewItems update,
// otherwise the UI thread (RoomScreen) won't be able to correctly locate the target event.
if let Some((index, found_event_id)) = found_target_event_id.take() {
target_event_id = None;
timeline_update_sender.send(
TimelineUpdate::TargetEventFound {
target_event_id: found_event_id.clone(),
index,
}
).unwrap_or_else(
|_e| panic!("Error: timeline update sender couldn't send TargetEventFound({found_event_id}, {index}) to room {room_id}, thread {thread_root_event_id:?}!")
);
}

// Send a Makepad-level signal to update this room's timeline UI view.
SignalToUI::set_ui_signal();
// Send a Makepad-level signal to update this room's timeline UI view.
SignalToUI::set_ui_signal();
} else {
// Closed: our local items are updated above; remember to catch the UI up on reopen.
has_unsent_changes = true;
}
}
}

Expand Down
Loading