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
661 changes: 661 additions & 0 deletions src/directory_search.rs

Large diffs are not rendered by default.

1,032 changes: 918 additions & 114 deletions src/home/add_room.rs

Large diffs are not rendered by default.

37 changes: 20 additions & 17 deletions src/home/room_screen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use ruma::{OwnedUserId, api::client::receipt::create_receipt::v3::ReceiptType, e
use matrix_sdk_ui::sync_service::State;
use crate::{
app::{AppStateAction, ConfirmDeleteAction, SelectedRoom}, event_preview::{plaintext_body_of_timeline_item, text_preview_of_thread_reply, text_preview_of_timeline_item}, home::{edited_indicator::EditedIndicatorWidgetRefExt, invite_modal::InviteModalAction, link_preview::{LinkPreviewCache, LinkPreviewRef, LinkPreviewWidgetRefExt}, loading_pane::LoadingPaneWidgetExt, navigation_tab_bar::NavigationBarAction, room_image_viewer::{fetch_full_image_for_viewer, get_image_file_details}, rooms_list::{RoomsListAction, RoomsListRef, RoomsListUpdate, enqueue_rooms_list_update}, rooms_list_header::RoomsListHeaderAction, tombstone_footer::SuccessorRoomDetails}, media_cache::{get_image_cache_key, MediaCache, MediaCacheEntry}, profile::{
user_profile::{ShowUserProfileAction, UserProfile, UserProfileAndRoomId, UserProfilePaneAction, UserProfilePaneInfo, UserProfileSlidingPaneRef, UserProfileSlidingPaneWidgetExt},
user_profile::{RoomMembershipInfo, ShowUserProfileAction, UserProfile, UserProfileAndRoomId, UserProfilePaneAction, UserProfilePaneInfo, UserProfileSlidingPaneRef, UserProfileSlidingPaneWidgetExt},
user_profile_cache,
},
room::{reply_preview::{CollapsiblePreviewRef, CollapsiblePreviewWidgetRefExt}, room_input_bar::{RoomInputBarState, RoomInputBarWidgetRefExt}, typing_notice::TypingNoticeWidgetExt},
Expand Down Expand Up @@ -1404,17 +1404,20 @@ impl Widget for RoomScreen {
}

// Handle the action that requests to show the user profile sliding pane.
if let ShowUserProfileAction::ShowUserProfile(profile_and_room_id) = action.as_widget_action().cast() {
if let ShowUserProfileAction::ShowUserProfile(UserProfileAndRoomId { user_profile, room_id }) = action.as_widget_action().cast() {
self.show_user_profile(
cx,
&user_profile_sliding_pane,
UserProfilePaneInfo {
profile_and_room_id,
room_name: self.room_name_id.as_ref().map_or_else(
|| UNNAMED_ROOM.to_string(),
|r| r.to_string(),
),
room_member: None,
user_profile,
room: Some(RoomMembershipInfo {
room_id,
room_name: self.room_name_id.as_ref().map_or_else(
|| UNNAMED_ROOM.to_string(),
|r| r.to_string(),
),
room_member: None,
}),
},
);
}
Expand Down Expand Up @@ -2381,16 +2384,16 @@ impl RoomScreen {
cx,
pane,
UserProfilePaneInfo {
profile_and_room_id: UserProfileAndRoomId {
user_profile: UserProfile {
user_id,
username: None,
avatar_state: AvatarState::Unknown,
},
room_id: room_name_id.room_id().clone(),
user_profile: UserProfile {
user_id,
username: None,
avatar_state: AvatarState::Unknown,
},
room_name: room_name_id.to_string(),
room_member: None,
room: Some(RoomMembershipInfo {
room_id: room_name_id.room_id().clone(),
room_name: room_name_id.to_string(),
room_member: None,
}),
},
);
return true;
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ pub mod tsp_dummy;
pub mod sliding_sync;
pub mod space_service_sync;
pub mod threads_list_sync;
pub mod directory_search;
pub mod avatar_cache;
pub mod room_preview_cache;
pub mod media_cache;
Expand Down
77 changes: 49 additions & 28 deletions src/profile/user_profile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ script_mod! {
}
}

LineH { padding: 15 }
membership_divider := LineH { padding: 15 }

action_buttons := View {
width: Fill, height: Fit
Expand Down Expand Up @@ -305,22 +305,30 @@ pub enum UserProfilePaneAction {
/// Information needed to populate/display the user profile sliding pane.
#[derive(Clone, Debug)]
pub struct UserProfilePaneInfo {
pub profile_and_room_id: UserProfileAndRoomId,
pub room_name: String,
pub room_member: Option<RoomMember>,
pub user_profile: UserProfile,
/// This is `None` if the user was found outside of a room, e.g., by a search.
pub room: Option<RoomMembershipInfo>,
}
impl Deref for UserProfilePaneInfo {
type Target = UserProfileAndRoomId;
type Target = UserProfile;
fn deref(&self) -> &Self::Target {
&self.profile_and_room_id
&self.user_profile
}
}
impl DerefMut for UserProfilePaneInfo {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.profile_and_room_id
&mut self.user_profile
}
}
impl UserProfilePaneInfo {

/// The room that the user profile sliding pane shows a user's membership in.
#[derive(Clone, Debug)]
pub struct RoomMembershipInfo {
pub room_id: OwnedRoomId,
pub room_name: String,
pub room_member: Option<RoomMember>,
}
impl RoomMembershipInfo {
fn membership_title(&self) -> String {
if self.room_name.is_empty() {
format!("Membership in Room {}", self.room_id.as_str())
Expand Down Expand Up @@ -385,6 +393,7 @@ pub struct UserProfileSlidingPane {
#[live] overlay_bg_color: Vec4f,

#[rust] info: Option<UserProfilePaneInfo>,
#[rust] is_showing_current_account: bool,
#[rust] is_animating_out: bool,
/// Held while this pane is shown such that `Escape` closes this pane.
/// This scope is dropped when the pane starts animating out or is reset.
Expand Down Expand Up @@ -474,20 +483,25 @@ impl Widget for UserProfileSlidingPane {
&& let Some((new_profile, room_member)) = user_profile_cache::with_user_profile(
cx,
our_info.user_id.clone(),
Some(&our_info.room_id),
our_info.room.as_ref().map(|room| &room.room_id),
false,
|profile, rooms| (profile.clone(), rooms.get(&our_info.room_id).and_then(|entry| entry.loaded()).cloned())
|profile, rooms| (
profile.clone(),
our_info.room.as_ref().and_then(|room| rooms.get(&room.room_id)).and_then(|entry| entry.loaded()).cloned(),
)
)
{
our_info.user_profile = new_profile;
our_info.room_member = room_member;
// Use the avatar URI from the `room_member`, as it will be the most up-to-date
// and specific to the room that this user profile sliding pane is currently being shown for.
if let Some(avatar_uri) = our_info.room_member.as_ref()
if let Some(avatar_uri) = room_member.as_ref()
.and_then(|rm| rm.avatar_url().map(|u| u.to_owned()))
{
our_info.avatar_state = AvatarState::Known(Some(avatar_uri));
}
if let Some(room) = our_info.room.as_mut() {
room.room_member = room_member;
}
// If we know the avatar URI, try to get/fetch the actual avatar image data.
our_info.avatar_state.update_from_cache(cx);
self.redraw(cx);
Expand Down Expand Up @@ -591,26 +605,29 @@ impl Widget for UserProfileSlidingPane {
.unwrap_or_else(|| avatar_ref.show_text(cx, None, None, info.displayable_name()));

// Set the membership status and role in the room.
self.label(cx, ids!(membership_title_label)).set_text(cx, &info.membership_title());
self.label(cx, ids!(membership_status_label)).set_text(cx, info.membership_status());
self.label(cx, ids!(role_info_label)).set_text(cx, info.role_in_room().as_ref());
self.view(cx, ids!(membership)).set_visible(cx, info.room.is_some());
self.view(cx, ids!(membership_divider)).set_visible(cx, info.room.is_some());
if let Some(room) = info.room.as_ref() {
self.label(cx, ids!(membership_title_label)).set_text(cx, &room.membership_title());
self.label(cx, ids!(membership_status_label)).set_text(cx, room.membership_status());
self.label(cx, ids!(role_info_label)).set_text(cx, room.role_in_room().as_ref());
}

// Draw and show/hide the buttons according to user and room membership info:
// * `direct_message_button` is hidden if the user is the same as the account user,
// since you cannot direct message yourself.
// * `copy_link_to_user_button` is always enabled with the same text.
// * `jump_to_read_receipt_button` is always shown with the same text.
// * `jump_to_read_receipt_button` is hidden if there's no room, since there's no timeline to jump in.
// * `block_user_button` is hidden if the user is the same as the account user,
// since you cannot block yourself.
// * The button text changes to "Unblock" if the user is already blocked.
let is_pane_showing_current_account = info.room_member.as_ref()
.map(|rm| rm.is_account_user())
.unwrap_or_else(|| current_user_id().is_some_and(|uid| uid == info.user_id));

self.button(cx, ids!(direct_message_button)).set_visible(cx, !is_pane_showing_current_account);
self.button(cx, ids!(direct_message_button)).set_visible(cx, !self.is_showing_current_account);
if info.room.is_none() {
self.button(cx, ids!(jump_to_read_receipt_button)).set_visible(cx, false);
}

let block_user_button = self.button(cx, ids!(block_user_button));
block_user_button.set_visible(cx, !is_pane_showing_current_account);
block_user_button.set_visible(cx, !self.is_showing_current_account);
// Unfortunately the Matrix SDK's RoomMember type does not properly track
// the blocked state of a user, so we have to maintain it separately.
let is_blocked = is_user_blocked(&info.user_id);
Expand All @@ -632,18 +649,19 @@ impl UserProfileSlidingPane {

/// Sets the info to be displayed in this user profile sliding pane.
///
/// If the `room_member` field is `None`, this function will attempt to
/// retrieve the user's room membership info from the local cache,
/// If there's no `room_member` (or no `room` at all), this function will attempt to
/// retrieve the user's profile and room membership info from the local cache,
/// and then submit a request to asynchronously fetch it from the server
/// if it's not found in the cache.
pub fn set_info(&mut self, cx: &mut Cx, mut info: UserProfilePaneInfo) {
if info.room_member.is_none() {
if info.room.as_ref().is_none_or(|room| room.room_member.is_none()) {
let room_id = info.room.as_ref().map(|room| &room.room_id);
match user_profile_cache::with_user_profile(
cx,
info.user_id.clone(),
Some(&info.room_id),
room_id,
true,
|profile, rooms| (profile.clone(), rooms.get(&info.room_id).and_then(|entry| entry.loaded()).cloned())
|profile, rooms| (profile.clone(), room_id.and_then(|id| rooms.get(id)).and_then(|entry| entry.loaded()).cloned())
) {
Some((new_profile, Some(room_member))) => {
log!("Found user {} room member info in cache", info.user_id);
Expand All @@ -665,7 +683,9 @@ impl UserProfileSlidingPane {
.map(|dn| dn.to_owned())
.or_else(|| new_profile.username.clone());
}
info.room_member = Some(room_member);
if let Some(room) = info.room.as_mut() {
room.room_member = Some(room_member);
}
}
// We don't have their room member info, so fill in what we can from their profile.
Some((new_profile, None)) => {
Expand All @@ -688,6 +708,7 @@ impl UserProfileSlidingPane {
.show(cx, info.user_id.clone());
}

self.is_showing_current_account = current_user_id().is_some_and(|uid| uid == info.user_id);
self.info = Some(info);
}

Expand Down
15 changes: 15 additions & 0 deletions src/profile/user_profile_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,21 @@ impl RoomMemberEntry {
}
}

/// Inserts the given user profile into the cache, unless the cache already has an entry for that user.
///
/// This lets us show a profile that we already got elsewhere, e.g., from a search, without fetching it again.
pub fn insert_user_profile_if_missing(cx: &mut Cx, user_profile: UserProfile) {
let user_id = user_profile.user_id.clone();
let was_missing = USER_PROFILE_CACHE.with_borrow_mut(|cache| {
let Entry::Vacant(entry) = cache.entry(user_id.clone()) else { return false };
entry.insert(UserProfileCacheEntry::Loaded { user_profile, rooms: BTreeMap::new() });
true
});
if was_missing {
cx.action(UserProfilesUpdated { user_ids: BTreeSet::from([user_id]) });
}
}

/// Inserts already-known room member info for the given room into the cache,
/// emitting [`UserProfilesUpdated`] for that user.
///
Expand Down
6 changes: 5 additions & 1 deletion src/room/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use std::sync::Arc;
use makepad_widgets::ScriptVm;
use matrix_sdk::{RoomDisplayName, RoomHeroWithProfile, RoomState, room_preview::RoomPreview};
use ruma::{OwnedRoomAliasId, OwnedRoomId, OwnedRoomOrAliasId, OwnedServerName, room::{JoinRuleSummary, RoomType}};
use ruma::{OwnedMxcUri, OwnedRoomAliasId, OwnedRoomId, OwnedRoomOrAliasId, OwnedServerName, room::{JoinRuleSummary, RoomType}};

use crate::shared::avatar::AvatarImage;
use crate::utils::RoomNameId;
Expand Down Expand Up @@ -42,6 +42,7 @@ pub fn script_mod(vm: &mut ScriptVm) {
/// but haven't yet fetched the full room preview,
/// such as a room to navigate to, or a room to join.
#[derive(Clone, Debug)]
#[allow(clippy::large_enum_variant)]
pub enum BasicRoomDetails {
/// We only know the room's ID, so we'll need to fetch the room preview
/// to get more info about the room.
Expand Down Expand Up @@ -155,6 +156,8 @@ pub struct FetchedRoomPreview {
pub canonical_alias: Option<OwnedRoomAliasId>,
/// The room's topic, if set.
pub topic: Option<String>,
/// The room's avatar URL, if set.
pub avatar_url: Option<OwnedMxcUri>,
/// The number of joined members.
pub num_joined_members: u64,
/// The number of active members, if known (joined + invited).
Expand Down Expand Up @@ -193,6 +196,7 @@ impl FetchedRoomPreview {
alias,
canonical_alias: room_preview.canonical_alias,
topic: room_preview.topic,
avatar_url: room_preview.avatar_url,
num_joined_members: room_preview.num_joined_members,
num_active_members: room_preview.num_active_members,
room_type: room_preview.room_type,
Expand Down
15 changes: 8 additions & 7 deletions src/room/room_members_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use matrix_sdk::{room::{RoomMember, RoomMemberRole}, ruma::{OwnedRoomId, events:

use crate::{
profile::{
user_profile::{UserProfile, UserProfileAndRoomId, UserProfilePaneInfo, UserProfileSlidingPaneRef, member_display_name, role_name},
user_profile::{RoomMembershipInfo, UserProfile, UserProfilePaneInfo, UserProfileSlidingPaneRef, member_display_name, role_name},
user_profile_cache,
},
room::pane_dock::FRAME_PADDING,
Expand Down Expand Up @@ -203,7 +203,8 @@ impl Widget for RoomMembersList {
self.redraw(cx);
}

let (clicked, hover_changed) = handle_row_actions(&self.members_list(), actions, &mut self.hovered_index);
let list = self.members_list();
let (clicked, hover_changed) = handle_row_actions(list.items_with_actions(actions), list.was_scrolling(), actions, &mut self.hovered_index);
if hover_changed {
self.redraw(cx);
}
Expand Down Expand Up @@ -401,12 +402,12 @@ pub fn show_member_profile(
// Cache the member info first, otherwise the pane would drop it upon its next refresh.
user_profile_cache::insert_room_member(cx, room_name_id.room_id().clone(), member.clone());
pane.set_info(cx, UserProfilePaneInfo {
profile_and_room_id: UserProfileAndRoomId {
user_profile: UserProfile::from(&member),
user_profile: UserProfile::from(&member),
room: Some(RoomMembershipInfo {
room_id: room_name_id.room_id().clone(),
},
room_name: room_name_id.to_string(),
room_member: Some(member),
room_name: room_name_id.to_string(),
room_member: Some(member),
}),
});
pane.show(cx);
}
Loading
Loading