diff --git a/src/session/view/content/room_history/state/content.rs b/src/session/view/content/room_history/state/content.rs index 7a7796eb..e2a17bf0 100644 --- a/src/session/view/content/room_history/state/content.rs +++ b/src/session/view/content/room_history/state/content.rs @@ -1,6 +1,6 @@ use adw::{prelude::*, subclass::prelude::*}; use gettextrs::gettext; -use gtk::{glib, pango}; +use gtk::{glib, glib::clone, pango}; use matrix_sdk_ui::timeline::{ AnyOtherFullStateEventContent, MemberProfileChange, MembershipChange, OtherState, RoomMembershipChange, TimelineItemContent, @@ -12,7 +12,12 @@ use ruma::{ use tracing::warn; use super::StateCreation; -use crate::{gettext_f, prelude::*, session::model::Event}; +use crate::{ + gettext_f, + prelude::*, + session::model::{Event, Member}, + utils::BoundObjectWeakRef, +}; mod imp { use super::*; @@ -23,6 +28,8 @@ mod imp { /// The state event displayed by this widget. #[property(get, set = Self::set_event, nullable)] event: glib::WeakRef, + /// The sender of the event. + sender: BoundObjectWeakRef, } #[glib::object_subclass] @@ -46,26 +53,45 @@ mod imp { return; }; + let sender = event.sender(); + let disambiguated_name_handler = sender.connect_disambiguated_name_notify(clone!( + #[weak(rename_to = imp)] + self, + move |_| { + imp.update_content(); + } + )); + self.sender.set(&sender, vec![disambiguated_name_handler]); + + self.event.set(Some(event)); + self.update_content(); + } + + /// Update the content for the current state. + fn update_content(&self) { + let Some(event) = self.event.upgrade() else { + return; + }; + let Some(sender) = self.sender.obj() else { + return; + }; + match event.content() { TimelineItemContent::MembershipChange(membership_change) => { - self.update_with_membership_change(&membership_change, &event.sender_id()); + self.update_with_membership_change(&membership_change, &sender); + } + TimelineItemContent::ProfileChange(profile_change) => { + self.update_with_profile_change(&profile_change, &sender); } - TimelineItemContent::ProfileChange(profile_change) => self - .update_with_profile_change( - &profile_change, - &event.sender().disambiguated_name(), - ), TimelineItemContent::OtherState(other_state) => { - self.update_with_other_state(&other_state); + self.update_with_other_state(&other_state, &sender); } _ => unreachable!(), } - - self.event.set(Some(event)); } /// Update this row with the given [`OtherState`]. - fn update_with_other_state(&self, other_state: &OtherState) { + fn update_with_other_state(&self, other_state: &OtherState, sender: &Member) { let widget = match other_state.content() { AnyOtherFullStateEventContent::RoomCreate(content) => { WidgetType::Creation(StateCreation::new(content)) @@ -86,8 +112,11 @@ mod imp { WidgetType::Text(gettext_f( // Translators: Do NOT translate the content between '{' and '}', this is a // variable name. - "{user} was invited to this room.", - &[("user", display_name)], + "{sender} invited {user}.", + &[ + ("sender", &sender.disambiguated_name()), + ("user", display_name), + ], )) } _ => { @@ -113,9 +142,10 @@ mod imp { fn update_with_membership_change( &self, membership_change: &RoomMembershipChange, - sender: &UserId, + sender: &Member, ) { - let display_name = match membership_change.content() { + let sender_display_name = sender.disambiguated_name(); + let target_display_name = match membership_change.content() { FullStateEventContent::Original { content, .. } => content .displayname .clone() @@ -124,66 +154,81 @@ mod imp { }; let supported_membership_change = - Self::to_supported_membership_change(membership_change, sender); + Self::to_supported_membership_change(membership_change, sender.user_id()); let message = match supported_membership_change { MembershipChange::Joined => { // Translators: Do NOT translate the content between '{' and '}', this // is a variable name. - gettext_f("{user} joined this room.", &[("user", &display_name)]) + gettext_f( + "{user} joined this room.", + &[("user", &target_display_name)], + ) } MembershipChange::Left => { // Translators: Do NOT translate the content between '{' and '}', // this is a variable name. - gettext_f("{user} left the room.", &[("user", &display_name)]) + gettext_f("{user} left the room.", &[("user", &target_display_name)]) } - MembershipChange::Banned => gettext_f( + MembershipChange::Banned | MembershipChange::KickedAndBanned => gettext_f( // Translators: Do NOT translate the content between - // '{' and '}', this is a variable name. - "{user} was banned.", - &[("user", &display_name)], + // '{' and '}', these are variable names. + "{sender} banned {user}.", + &[ + ("sender", &sender_display_name), + ("user", &target_display_name), + ], ), MembershipChange::Unbanned => gettext_f( // Translators: Do NOT translate the content between - // '{' and '}', this is a variable name. - "{user} was unbanned.", - &[("user", &display_name)], + // '{' and '}', these are variable names. + "{sender} unbanned {user}.", + &[ + ("sender", &sender_display_name), + ("user", &target_display_name), + ], ), MembershipChange::Kicked => gettext_f( // Translators: Do NOT translate the content between '{' and - // '}', this is a variable name. - "{user} was kicked out of the room.", - &[("user", &display_name)], + // '}', these are variable names. + "{sender} kicked {user} out.", + &[ + ("sender", &sender_display_name), + ("user", &target_display_name), + ], ), MembershipChange::Invited | MembershipChange::KnockAccepted => gettext_f( - // Translators: Do NOT translate the content between '{' and '}', this is - // a variable name. - "{user} was invited to this room.", - &[("user", &display_name)], - ), - MembershipChange::KickedAndBanned => gettext_f( - // Translators: Do NOT translate the content between '{' and '}', this is - // a variable name. - "{user} was kicked out of the room and banned.", - &[("user", &display_name)], + // Translators: Do NOT translate the content between '{' and '}', these are + // variable names. + "{sender} invited {user}.", + &[ + ("sender", &sender_display_name), + ("user", &target_display_name), + ], ), MembershipChange::InvitationAccepted => gettext_f( // Translators: Do NOT translate the content between // '{' and '}', this is a variable name. "{user} accepted the invite.", - &[("user", &display_name)], + &[("user", &target_display_name)], ), MembershipChange::InvitationRejected => gettext_f( // Translators: Do NOT translate the content between - // '{' and '}', this is a variable name. - "{user} rejected the invite.", - &[("user", &display_name)], + // '{' and '}', these are variable names. + "{user} declined the invite.", + &[ + ("sender", &sender_display_name), + ("user", &target_display_name), + ], ), MembershipChange::InvitationRevoked => gettext_f( // Translators: Do NOT translate the content between - // '{' and '}', this is a variable name. - "The invitation for {user} has been revoked.", - &[("user", &display_name)], + // '{' and '}', these are variable names. + "{sender} revoked the invitation for {user}.", + &[ + ("sender", &sender_display_name), + ("user", &target_display_name), + ], ), MembershipChange::Knocked => // TODO: Add button to invite the user. @@ -192,20 +237,23 @@ mod imp { // Translators: Do NOT translate the content between '{' and '}', this // is a variable name. "{user} requested to be invited to this room.", - &[("user", &display_name)], + &[("user", &target_display_name)], ) } MembershipChange::KnockRetracted => gettext_f( // Translators: Do NOT translate the content between // '{' and '}', this is a variable name. "{user} retracted their request to be invited to this room.", - &[("user", &display_name)], + &[("user", &target_display_name)], ), MembershipChange::KnockDenied => gettext_f( // Translators: Do NOT translate the content between - // '{' and '}', this is a variable name. - "{user}’s request to be invited to this room was denied.", - &[("user", &display_name)], + // '{' and '}', these are variable names. + "{sender} denied {user}’s request to be invited to this room.", + &[ + ("sender", &sender_display_name), + ("user", &target_display_name), + ], ), _ => { warn!( @@ -271,7 +319,7 @@ mod imp { fn update_with_profile_change( &self, profile_change: &MemberProfileChange, - display_name: &str, + sender: &Member, ) { let message = if let Some(displayname) = profile_change.displayname_change() { if let Some(prev_name) = &displayname.old { @@ -309,33 +357,38 @@ mod imp { ) } } else if let Some(avatar_url) = profile_change.avatar_url_change() { + let display_name = sender.disambiguated_name(); + if avatar_url.old.is_none() { gettext_f( // Translators: Do NOT translate the content between // '{' and '}', this is a variable name. "{user} set their avatar.", - &[("user", display_name)], + &[("user", &display_name)], ) } else if avatar_url.new.is_none() { gettext_f( // Translators: Do NOT translate the content between // '{' and '}', this is a variable name. "{user} removed their avatar.", - &[("user", display_name)], + &[("user", &display_name)], ) } else { gettext_f( // Translators: Do NOT translate the content between // '{' and '}', this is a variable name. "{user} changed their avatar.", - &[("user", display_name)], + &[("user", &display_name)], ) } } else { // We don't know what changed so fall back to the membership. // Translators: Do NOT translate the content between '{' and '}', this // is a variable name. - gettext_f("{user} joined this room.", &[("user", display_name)]) + gettext_f( + "{user} joined this room.", + &[("user", &sender.disambiguated_name())], + ) }; let child = self.obj().child_or_else::(text);