Skip to content

Commit

Permalink
Merge pull request #102 from kevinaboos/post_ignore_sync_pagination
Browse files Browse the repository at this point in the history
Post-`ignore_user` pagination does work to refill the cleared timeline.
  • Loading branch information
kevinaboos authored Aug 2, 2024
2 parents 343d52c + 20da196 commit 8a60937
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 13 deletions.
1 change: 1 addition & 0 deletions src/home/room_screen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -995,6 +995,7 @@ impl Widget for Timeline {
if clear_cache {
tl.content_drawn_since_last_update.clear();
tl.profile_drawn_since_last_update.clear();
tl.fully_paginated = false;
} else {
tl.content_drawn_since_last_update.remove(changed_indices.clone());
tl.profile_drawn_since_last_update.remove(changed_indices.clone());
Expand Down
11 changes: 7 additions & 4 deletions src/profile/user_profile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crossbeam_queue::SegQueue;
use makepad_widgets::*;
use matrix_sdk::{room::{RoomMember, RoomMemberRole}, ruma::{events::room::member::MembershipState, OwnedRoomId, OwnedUserId, UserId}};
use crate::{
shared::avatar::AvatarWidgetExt, sliding_sync::{get_client, submit_async_request, MatrixRequest}, utils
shared::avatar::AvatarWidgetExt, sliding_sync::{get_client, is_user_ignored, submit_async_request, MatrixRequest}, utils
};

thread_local! {
Expand Down Expand Up @@ -728,9 +728,12 @@ impl Widget for UserProfileSlidingPane {

let ignore_user_button = self.button(id!(ignore_user_button));
ignore_user_button.set_enabled(!is_pane_showing_current_account && info.room_member.is_some());
ignore_user_button.set_text(info.room_member.as_ref()
.and_then(|rm| rm.is_ignored().then_some("Unignore (Unblock) User"))
.unwrap_or("Ignore (Block) User")
// Unfortunately the Matrix SDK's RoomMember type does not properly track
// the `ignored` state of a user, so we have to maintain it separately.
let is_ignored = info.room_member.as_ref()
.is_some_and(|rm| is_user_ignored(rm.user_id()));
ignore_user_button.set_text(
if is_ignored { "Unignore (Unblock) User" } else { "Ignore (Block) User" }
);

self.view.draw_walk(cx, scope, walk)
Expand Down
91 changes: 82 additions & 9 deletions src/sliding_sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ use imbl::Vector;
use makepad_widgets::{error, log, SignalToUI};
use matrix_sdk::{
config::RequestConfig, media::MediaRequest, room::RoomMember, ruma::{
api::client::session::get_login_types::v3::LoginType, assign, events::{room::message::RoomMessageEventContent, FullStateEventContent, StateEventType}, MilliSecondsSinceUnixEpoch, OwnedMxcUri, OwnedRoomAliasId, OwnedRoomId, OwnedUserId, UInt
}, sliding_sync::http::request::{AccountData, E2EE, ListFilters, ToDevice}, Client, Room, SlidingSyncList, SlidingSyncMode
api::client::session::get_login_types::v3::LoginType, assign, events::{room::message::RoomMessageEventContent, FullStateEventContent, StateEventType}, MilliSecondsSinceUnixEpoch, OwnedMxcUri, OwnedRoomAliasId, OwnedRoomId, OwnedUserId, UInt, UserId
}, sliding_sync::http::request::{AccountData, ListFilters, ToDevice, E2EE}, Client, Room, SlidingSyncList, SlidingSyncMode
};
use matrix_sdk_ui::{timeline::{AnyOtherFullStateEventContent, LiveBackPaginationStatus, TimelineItem, TimelineItemContent}, Timeline};
use tokio::{
runtime::Handle,
sync::mpsc::{UnboundedSender, UnboundedReceiver}, task::JoinHandle,
};
use unicode_segmentation::UnicodeSegmentation;
use std::{cmp::{max, min}, collections::BTreeMap, ops::Range, sync::{Arc, Mutex, OnceLock}};
use std::{cmp::{max, min}, collections::{BTreeMap, BTreeSet}, ops::Range, sync::{Arc, Mutex, OnceLock}};
use url::Url;

use crate::{home::{room_screen::TimelineUpdate, rooms_list::{self, enqueue_rooms_list_update, RoomPreviewAvatar, RoomPreviewEntry, RoomsListUpdate}}, media_cache::{MediaCacheEntry, AVATAR_CACHE}, profile::user_profile::{enqueue_user_profile_update, UserProfile, UserProfileUpdate}, utils::MEDIA_THUMBNAIL_FORMAT};
Expand Down Expand Up @@ -372,10 +372,6 @@ async fn async_worker(mut receiver: UnboundedReceiver<MatrixRequest>) -> Result<
if ignore_result.is_err() {
return;
}
// After successfully (un)ignoring a user, all timelines are fully cleared by the Matrix SDK.
// Therefore, we need to re-fetch all timelines for all rooms,
// and currently the only way to actually accomplish this is via pagination.
// See: <https://github.com/matrix-org/matrix-rust-sdk/issues/1703#issuecomment-2250297923>

// We need to re-acquire the `RoomMember` object now that its state
// has changed, i.e., the user has been (un)ignored).
Expand All @@ -388,11 +384,25 @@ async fn async_worker(mut receiver: UnboundedReceiver<MatrixRequest>) -> Result<
if new_room_member.is_ignored() { "" } else { "un" },
);
enqueue_user_profile_update(UserProfileUpdate::RoomMemberOnly {
room_id,
room_id: room_id.clone(),
room_member: new_room_member,
});
}
}

// After successfully (un)ignoring a user, all timelines are fully cleared by the Matrix SDK.
// Therefore, we need to re-fetch all timelines for all rooms,
// and currently the only way to actually accomplish this is via pagination.
// See: <https://github.com/matrix-org/matrix-rust-sdk/issues/1703#issuecomment-2250297923>
//
// Note that here we only proactively re-paginate the *current* room
// (the one being viewed by the user when this ignore request was issued),
// and all other rooms will be re-paginated in `handle_ignore_user_list_subscriber()`.`
submit_async_request(MatrixRequest::PaginateRoomTimeline {
room_id,
num_events: 50,
forwards: false,
});
});
}

Expand Down Expand Up @@ -560,6 +570,22 @@ pub fn get_client() -> Option<Client> {
CLIENT.get().cloned()
}

/// The list of users that the current user has chosen to ignore.
/// Ideally we shouldn't have to maintain this list ourselves,
/// but the Matrix SDK doesn't currently properly maintain the list of ignored users.
static IGNORED_USERS: Mutex<BTreeSet<OwnedUserId>> = Mutex::new(BTreeSet::new());

/// Returns a deep clone of the current list of ignored users.
pub fn get_ignored_users() -> BTreeSet<OwnedUserId> {
IGNORED_USERS.lock().unwrap().clone()
}

/// Returns whether the given user ID is currently being ignored.
pub fn is_user_ignored(user_id: &UserId) -> bool {
IGNORED_USERS.lock().unwrap().contains(user_id)
}


/// Returns the timeline update sender and receiver endpoints for the given room,
/// if and only if the receiver exists.
///
Expand Down Expand Up @@ -638,6 +664,9 @@ async fn async_main_loop() -> Result<()> {
let (client, _token) = login(cli).await?;
CLIENT.set(client.clone()).expect("BUG: CLIENT already set!");

// Listen for updates to the ignored user list.
handle_ignore_user_list_subscriber(client.clone());

let mut filters = ListFilters::default();
filters.not_room_types = vec!["m.space".into()]; // Ignore spaces for now.

Expand Down Expand Up @@ -707,7 +736,7 @@ async fn async_main_loop() -> Result<()> {
Some(Err(e)) => {
error!("sync loop was stopped by client error processing: {e}");
stream_error = Some(e);
continue;
break;
}
None => {
error!("sync loop ended unexpectedly");
Expand Down Expand Up @@ -889,6 +918,50 @@ async fn async_main_loop() -> Result<()> {
}


#[allow(unused)]
async fn current_ignore_user_list(client: &Client) -> Option<BTreeSet<OwnedUserId>> {
use matrix_sdk::ruma::events::ignored_user_list::IgnoredUserListEventContent;
let ignored_users = client.account()
.account_data::<IgnoredUserListEventContent>()
.await
.ok()??
.deserialize()
.ok()?
.ignored_users
.into_keys()
.collect();

Some(ignored_users)
}


fn handle_ignore_user_list_subscriber(client: Client) {
let mut subscriber = client.subscribe_to_ignore_user_list_changes();
Handle::current().spawn(async move {
while let Some(ignore_list) = subscriber.next().await {
log!("Received an updated ignored-user list: {ignore_list:?}");
let ignored_users_set = ignore_list
.into_iter()
.filter_map(|u| OwnedUserId::try_from(u).ok())
.collect::<BTreeSet<_>>();
*IGNORED_USERS.lock().unwrap() = ignored_users_set;

// After successfully (un)ignoring a user, all timelines are fully cleared by the Matrix SDK.
// Therefore, we need to re-fetch all timelines for all rooms,
// and currently the only way to actually accomplish this is via pagination.
// See: <https://github.com/matrix-org/matrix-rust-sdk/issues/1703#issuecomment-2250297923>
for joined_room in client.joined_rooms() {
submit_async_request(MatrixRequest::PaginateRoomTimeline {
room_id: joined_room.room_id().to_owned(),
num_events: 50,
forwards: false,
});
}
}
});
}


async fn timeline_subscriber_handler(
room_id: OwnedRoomId,
timeline: Arc<Timeline>,
Expand Down

0 comments on commit 8a60937

Please sign in to comment.