Skip to content
Open
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
148 changes: 109 additions & 39 deletions src/home/room_screen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2489,6 +2489,18 @@ impl Widget for RoomInfoSlidingPane {
}

if let Event::Actions(actions) = event {
for action in actions {
if action.as_widget_action().widget_uid_eq(self.widget_uid()).is_none()
&& let RoomInfoPaneAction::OpenPeopleProfile(user_id) = action.as_widget_action().cast()
{
cx.widget_action(
self.widget_uid(),
RoomInfoPaneAction::OpenPeopleProfile(user_id.clone()),
);
break;
}
}

if self.button(cx, ids!(header.back_button)).clicked(actions) {
self.show_people_page = false;
self.redraw(cx);
Expand Down Expand Up @@ -3234,9 +3246,7 @@ impl Widget for RoomScreen {
}
}
RoomInfoPaneAction::ShowPeoplePage => {
if let Some(tl) = self.tl_state.as_ref()
&& tl.room_members.is_none()
{
if let Some(tl) = self.tl_state.as_ref() {
submit_async_request(MatrixRequest::GetRoomMembers {
timeline_kind: tl.kind.clone(),
memberships: matrix_sdk::RoomMemberships::JOIN,
Expand All @@ -3256,6 +3266,8 @@ impl Widget for RoomScreen {
.as_ref()
.and_then(|member| member.avatar_url().map(ToOwned::to_owned))
);
let can_change_room_power_levels = self.tl_state.as_ref()
.is_some_and(|tl| tl.user_power.can_change_room_power_levels());
self.show_user_profile(
cx,
&user_profile_sliding_pane,
Expand All @@ -3270,6 +3282,7 @@ impl Widget for RoomScreen {
},
room_name: room_name_id.to_string(),
room_member,
can_change_room_power_levels,
},
);
}
Expand Down Expand Up @@ -3792,6 +3805,22 @@ 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() {
let mut profile_and_room_id = profile_and_room_id;
let room_member = self.tl_state.as_ref()
.and_then(|tl| tl.room_members.as_ref())
.and_then(|members| members.iter().find(|member| member.user_id() == profile_and_room_id.user_id).cloned());
if let Some(room_member) = room_member.as_ref() {
if profile_and_room_id.username.is_none() {
profile_and_room_id.username = room_member.display_name().map(ToOwned::to_owned);
}
if !profile_and_room_id.avatar_state.has_avatar() {
profile_and_room_id.avatar_state = AvatarState::Known(
room_member.avatar_url().map(ToOwned::to_owned)
);
}
}
let can_change_room_power_levels = self.tl_state.as_ref()
.is_some_and(|tl| tl.user_power.can_change_room_power_levels());
self.show_user_profile(
cx,
&user_profile_sliding_pane,
Expand All @@ -3801,7 +3830,8 @@ impl Widget for RoomScreen {
|| tr_key(self.app_language, "room_screen.fallback.unnamed_room").to_string(),
|r| r.to_string(),
),
room_member: None,
room_member,
can_change_room_power_levels,
},
);
}
Expand Down Expand Up @@ -4312,8 +4342,30 @@ impl RoomScreen {
self.close_leave_room_confirm_modal(cx);
}

fn resolved_app_service_bot_user_id(
&self,
app_state: &AppState,
room_id: &OwnedRoomId,
) -> Option<OwnedUserId> {
if let Some(bot_user_id) = app_state.bot_settings.bound_bot_user_id(room_id.as_ref()) {
return Some(bot_user_id.to_owned());
}

self.tl_state
.as_ref()
.filter(|tl| tl.kind.room_id() == room_id)
.and_then(|tl| tl.room_members.as_ref())
.and_then(|members|
detected_bot_binding_for_members(
app_state,
room_id,
members.as_ref(),
)
)
}

fn is_app_service_room_bound(&self, app_state: &AppState, room_id: &OwnedRoomId) -> bool {
app_state.bot_settings.is_room_bound(room_id)
self.resolved_app_service_bot_user_id(app_state, room_id).is_some()
}

fn send_app_service_feedback_message(&self, message: impl Into<String>) {
Expand Down Expand Up @@ -4357,7 +4409,8 @@ impl RoomScreen {
);
return false;
}
if !self.is_app_service_room_bound(app_state, &room_id) {
let bound_bot_user_id = self.resolved_app_service_bot_user_id(app_state, &room_id);
if bound_bot_user_id.is_none() {
self.send_app_service_feedback_message(
tr_key(self.app_language, "room_screen.popup.bot.bind_before_commands"),
);
Expand All @@ -4368,10 +4421,7 @@ impl RoomScreen {
timeline_kind,
message: RoomMessageEventContent::text_plain(command),
replied_to: None,
target_user_id: app_state
.bot_settings
.bound_bot_user_id(room_id.as_ref())
.map(ToOwned::to_owned),
target_user_id: bound_bot_user_id,
#[cfg(feature = "tsp")]
sign_with_tsp: false,
});
Expand Down Expand Up @@ -4608,35 +4658,44 @@ impl RoomScreen {
}
}

if !self.pending_invited_users.is_empty() {
let start = changed_indices.start.min(new_items.len());
let end = changed_indices.end.min(new_items.len());
let mut accepted_users: Vec<OwnedUserId> = Vec::new();
for idx in start..end {
let Some(new_item) = new_items.get(idx) else { continue };
let TimelineItemKind::Event(event_tl_item) = new_item.kind() else { continue };
let TimelineItemContent::MembershipChange(membership_change) = event_tl_item.content() else { continue };
let accepted = matches!(
membership_change.change(),
Some(MembershipChange::InvitationAccepted)
| Some(MembershipChange::Joined)
);
if accepted {
let invited_user_id = event_tl_item.sender().to_owned();
if self.pending_invited_users.contains(&invited_user_id) {
accepted_users.push(invited_user_id);
}
}
let start = changed_indices.start.min(new_items.len());
let end = changed_indices.end.min(new_items.len());
let mut accepted_users: Vec<OwnedUserId> = Vec::new();
let mut room_members_changed = false;
for idx in start..end {
let Some(new_item) = new_items.get(idx) else { continue };
let TimelineItemKind::Event(event_tl_item) = new_item.kind() else { continue };
let TimelineItemContent::MembershipChange(membership_change) = event_tl_item.content() else { continue };
if is_append {
room_members_changed = true;
}
for accepted_user in accepted_users {
self.pending_invited_users.remove(&accepted_user);
enqueue_popup_notification(
format!("{accepted_user} accepted the invite and joined."),
PopupKind::Success,
Some(4.0),
);
let accepted = matches!(
membership_change.change(),
Some(MembershipChange::InvitationAccepted)
| Some(MembershipChange::Joined)
);
if accepted {
let invited_user_id = event_tl_item.sender().to_owned();
if self.pending_invited_users.contains(&invited_user_id) {
accepted_users.push(invited_user_id);
}
}
}
if room_members_changed {
submit_async_request(MatrixRequest::GetRoomMembers {
timeline_kind: tl.kind.clone(),
memberships: matrix_sdk::RoomMemberships::JOIN,
local_only: false,
});
}
for accepted_user in accepted_users {
self.pending_invited_users.remove(&accepted_user);
enqueue_popup_notification(
format!("{accepted_user} accepted the invite and joined."),
PopupKind::Success,
Some(4.0),
);
}

if prior_items_changed {
// If this RoomScreen is showing the loading pane and has an ongoing backwards pagination request,
Expand Down Expand Up @@ -5060,6 +5119,16 @@ impl RoomScreen {
let Some(room_name_id) = self.room_name_id.as_ref() else {
return false;
};
let room_member = self.tl_state.as_ref()
.and_then(|tl| tl.room_members.as_ref())
.and_then(|members| members.iter().find(|member| member.user_id() == user_id).cloned());
let username = room_member.as_ref()
.and_then(|member| member.display_name().map(ToOwned::to_owned));
let avatar_state = room_member.as_ref()
.and_then(|member| member.avatar_url().map(ToOwned::to_owned))
.map_or(AvatarState::Unknown, |avatar_url| AvatarState::Known(Some(avatar_url)));
let can_change_room_power_levels = self.tl_state.as_ref()
.is_some_and(|tl| tl.user_power.can_change_room_power_levels());
// There is no synchronous way to get the user's full profile info
// including the details of their room membership,
// so we fill in with the details we *do* know currently,
Expand All @@ -5073,14 +5142,15 @@ impl RoomScreen {
profile_and_room_id: UserProfileAndRoomId {
user_profile: UserProfile {
user_id: user_id.to_owned(),
username: None,
avatar_state: AvatarState::Unknown,
username,
avatar_state,
},
room_id: room_name_id.room_id().clone(),
},
room_name: room_name_id.to_string(),
// TODO: use the extra `via` parameters
room_member: None,
room_member,
can_change_room_power_levels,
},
);
true
Expand Down
89 changes: 89 additions & 0 deletions src/profile/user_profile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,52 @@ script_mod! {
}
text: "Unknown"
}

power_level_controls := View {
visible: false,
width: Fill,
height: Fit,
flow: Down,
spacing: 6,
margin: Inset{top: 8}

power_level_label := Label {
margin: Inset{ left: 7 }
width: Fill, height: Fit
flow: Flow.Right{wrap: true}
draw_text +: {
color: (MESSAGE_TEXT_COLOR),
text_style: MESSAGE_TEXT_STYLE { font_size: 11 },
}
text: "Power Level"
}

power_level_dropdown := DropDownFlat {
width: Fill
height: 40
align: Align{y: 0.5}
padding: Inset{left: 12, top: 11, bottom: 11, right: 30}
draw_text +: {
text_style: REGULAR_TEXT { font_size: 11.5 }
color: #333
color_hover: uniform(#222)
color_focus: uniform(#222)
color_down: uniform(#222)
}
draw_bg +: {
color: uniform(#fff)
color_hover: uniform(#F0F0F2)
color_focus: uniform(#F0F0F2)
color_down: uniform(#E8E8EA)
border_color: uniform(#CCC)
border_color_hover: uniform(#AAA)
border_color_focus: uniform((COLOR_ACTIVE_PRIMARY))
arrow_color: uniform(#888)
arrow_color_hover: uniform(#555)
}
labels: ["Default", "Moderator", "Admin"]
}
}
}

LineH { padding: 15 }
Expand Down Expand Up @@ -299,6 +345,7 @@ pub struct UserProfilePaneInfo {
pub profile_and_room_id: UserProfileAndRoomId,
pub room_name: String,
pub room_member: Option<RoomMember>,
pub can_change_room_power_levels: bool,
}
impl Deref for UserProfilePaneInfo {
type Target = UserProfileAndRoomId;
Expand Down Expand Up @@ -463,6 +510,33 @@ impl Widget for UserProfileSlidingPane {
let Some(info) = self.info.as_ref() else { return };

if let Event::Actions(actions) = event {
let power_level_dropdown = self.drop_down(cx, ids!(power_level_dropdown));
if power_level_dropdown.changed(actions).is_some()
&& info.can_change_room_power_levels
&& let Some(room_member) = info.room_member.as_ref()
&& !room_member.is_account_user()
{
let selected_item = power_level_dropdown.selected_item();
let selected_role = match selected_item {
0 => None,
1 => Some(RoomMemberRole::Moderator),
2 => Some(RoomMemberRole::Administrator),
_ => None,
};
let current_selected_item = match room_member.suggested_role_for_power_level() {
RoomMemberRole::Creator | RoomMemberRole::Administrator => 2,
RoomMemberRole::Moderator => 1,
RoomMemberRole::User => 0,
};
if selected_item != current_selected_item {
submit_async_request(MatrixRequest::SetRoomMemberPowerLevel {
room_id: info.room_id.clone(),
user_id: info.user_id.clone(),
room_member_role: selected_role,
});
}
}

if self.button(cx, ids!(direct_message_button)).clicked(actions) {
let create_encrypted = scope
.data
Expand Down Expand Up @@ -566,6 +640,21 @@ impl Widget for UserProfileSlidingPane {
.map(|rm| rm.is_account_user())
.unwrap_or_else(|| current_user_id().is_some_and(|uid| uid == info.user_id));

let show_power_level_controls = info.can_change_room_power_levels
&& !is_pane_showing_current_account
&& info.room_member.is_some();
self.view(cx, ids!(power_level_controls)).set_visible(cx, show_power_level_controls);
if show_power_level_controls
&& let Some(room_member) = info.room_member.as_ref()
{
let selected_item = match room_member.suggested_role_for_power_level() {
RoomMemberRole::Creator | RoomMemberRole::Administrator => 2,
RoomMemberRole::Moderator => 1,
RoomMemberRole::User => 0,
};
self.drop_down(cx, ids!(power_level_dropdown)).set_selected_item(cx, selected_item);
}

self.button(cx, ids!(direct_message_button)).set_visible(cx, !is_pane_showing_current_account);

let ignore_user_button = self.button(cx, ids!(ignore_user_button));
Expand Down
Loading
Loading