diff --git a/src/profile/user_profile.rs b/src/profile/user_profile.rs index 61b24c76..ae06e311 100644 --- a/src/profile/user_profile.rs +++ b/src/profile/user_profile.rs @@ -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! { @@ -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) diff --git a/src/sliding_sync.rs b/src/sliding_sync.rs index 54557323..34d87a05 100644 --- a/src/sliding_sync.rs +++ b/src/sliding_sync.rs @@ -6,8 +6,8 @@ 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::{ @@ -15,7 +15,7 @@ use tokio::{ 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}; @@ -570,6 +570,22 @@ pub fn get_client() -> Option { 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> = Mutex::new(BTreeSet::new()); + +/// Returns a deep clone of the current list of ignored users. +pub fn get_ignored_users() -> BTreeSet { + 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. /// @@ -720,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"); @@ -902,11 +918,33 @@ async fn async_main_loop() -> Result<()> { } +#[allow(unused)] +async fn current_ignore_user_list(client: &Client) -> Option> { + use matrix_sdk::ruma::events::ignored_user_list::IgnoredUserListEventContent; + let ignored_users = client.account() + .account_data::() + .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:?}"); + 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::>(); + *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,