diff --git a/data/resources/stylesheet/_room_history.scss b/data/resources/stylesheet/_room_history.scss index 1738e089..ebca4fd9 100644 --- a/data/resources/stylesheet/_room_history.scss +++ b/data/resources/stylesheet/_room_history.scss @@ -56,11 +56,11 @@ room-title { @include vendor.focus-ring(); - &.has-header { + &.has-avatar { margin-top: 6px; } - &:not(.has-header) { + &:not(.has-avatar) { .event-content { &:dir(ltr) { margin-left: 54px; @@ -206,7 +206,7 @@ sender-avatar { } state-group-row.room-history-row { - &:not(.has-header) { + &:not(.has-avatar) { .event-content { &:dir(ltr) { margin-left: 42px; diff --git a/src/session/model/room/timeline/event/mod.rs b/src/session/model/room/timeline/event/mod.rs index a67317cc..3c312617 100644 --- a/src/session/model/room/timeline/event/mod.rs +++ b/src/session/model/room/timeline/event/mod.rs @@ -18,7 +18,7 @@ use tracing::{debug, error}; mod reaction_group; mod reaction_list; -pub use self::{ +pub(crate) use self::{ reaction_group::{ReactionData, ReactionGroup}, reaction_list::ReactionList, }; @@ -53,11 +53,25 @@ pub enum MessageState { /// The read receipt of a user. #[derive(Clone, Debug)] -pub struct UserReadReceipt { +pub(crate) struct UserReadReceipt { /// The ID of the user. - pub user_id: OwnedUserId, + pub(crate) user_id: OwnedUserId, /// The data of the receipt. - pub receipt: Receipt, + pub(crate) receipt: Receipt, +} + +/// The state of the header of an event in the room history. +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, glib::Enum)] +#[enum_type(name = "EventHeaderState")] +pub enum EventHeaderState { + /// The full header is displayed, with an avatar, a sender name and the + /// timestamp. + #[default] + Full, + /// Only the timestamp is displayed. + TimestampOnly, + /// The header is hidden. + Hidden, } mod imp { @@ -134,9 +148,9 @@ mod imp { /// Whether this event has any read receipt. #[property(get = Self::has_read_receipts)] has_read_receipts: PhantomData, - /// Whether this event should show its header in the room history. - #[property(get, set = Self::set_show_header, explicit_notify)] - show_header: Cell, + /// The state of the header of the event in the room history. + #[property(get, set = Self::set_header_state, explicit_notify, builder(EventHeaderState::default()))] + header_state: Cell, } #[glib::object_subclass] @@ -462,14 +476,14 @@ mod imp { self.read_receipts().n_items() > 0 } - /// Set whether this event should show its header in the room history. - fn set_show_header(&self, show: bool) { - if self.show_header.get() == show { + /// Set the state of the header of the event in the room history. + fn set_header_state(&self, state: EventHeaderState) { + if self.header_state.get() == state { return; } - self.show_header.set(show); - self.obj().notify_show_header(); + self.header_state.set(state); + self.obj().notify_header_state(); } } } diff --git a/src/session/model/room/timeline/mod.rs b/src/session/model/room/timeline/mod.rs index 462b717c..eceb78d8 100644 --- a/src/session/model/room/timeline/mod.rs +++ b/src/session/model/room/timeline/mod.rs @@ -594,30 +594,30 @@ mod imp { let current_sender = current.sender_id(); if !current.can_show_header() { - current.set_show_header(false); + current.set_header_state(EventHeaderState::Hidden); previous_sender = None; previous_timestamp = None; continue; } - let show_header = if previous_sender + let header_state = if previous_sender .as_ref() .is_none_or(|previous_sender| current_sender != *previous_sender) { - // The sender is different, show header. - true + // The sender is different, show the full header. + EventHeaderState::Full } else if previous_timestamp .and_then(|ts| current.origin_server_ts().0.checked_sub(ts.0)) .is_some_and(|elapsed| u64::from(elapsed) >= MAX_TIME_BETWEEN_HEADERS) { - // Too much time has passed, show header. - true + // Too much time has passed, show the timestamp. + EventHeaderState::TimestampOnly } else { // Do not show header. - false + EventHeaderState::Hidden }; - current.set_show_header(show_header); + current.set_header_state(header_state); previous_sender = Some(current_sender); previous_timestamp = Some(current.origin_server_ts()); } diff --git a/src/session/view/content/room_history/event_row.rs b/src/session/view/content/room_history/event_row.rs index f3b31e5f..ca47a661 100644 --- a/src/session/view/content/room_history/event_row.rs +++ b/src/session/view/content/room_history/event_row.rs @@ -199,7 +199,7 @@ mod imp { /// Set the event presented by this row. fn set_event(&self, event: Option) { // Reinitialize the header. - self.obj().remove_css_class("has-header"); + self.obj().remove_css_class("has-avatar"); self.disconnect_event_signals(); diff --git a/src/session/view/content/room_history/message_row/mod.rs b/src/session/view/content/room_history/message_row/mod.rs index ac510b50..d2ba87a8 100644 --- a/src/session/view/content/room_history/message_row/mod.rs +++ b/src/session/view/content/room_history/message_row/mod.rs @@ -18,7 +18,11 @@ pub use self::content::{ContentFormat, MessageContent}; use self::{message_state_stack::MessageStateStack, reaction_list::MessageReactionList}; use super::{ReadReceiptsList, SenderAvatar}; use crate::{ - gettext_f, prelude::*, session::model::Event, system_settings::ClockFormat, utils::BoundObject, + gettext_f, + prelude::*, + session::model::{Event, EventHeaderState}, + system_settings::ClockFormat, + utils::BoundObject, Application, Window, }; @@ -56,11 +60,6 @@ mod imp { /// The event that is presented. #[property(get, set = Self::set_event, explicit_notify)] event: BoundObject, - /// Whether this item should show its header. - /// - /// This is ignored if this event doesn’t have a header. - #[property(get = Self::show_header, set = Self::set_show_header, explicit_notify)] - show_header: PhantomData, /// The texture of the image preview displayed by the descendant of this /// widget, if any. #[property(get = Self::texture)] @@ -137,31 +136,6 @@ mod imp { impl BinImpl for MessageRow {} impl MessageRow { - /// Whether this item should show its header. - /// - /// This is ignored if this event doesn’t have a header. - fn show_header(&self) -> bool { - self.avatar.is_visible() && self.header.is_visible() - } - - /// Set whether this item should show its header. - fn set_show_header(&self, visible: bool) { - let obj = self.obj(); - - self.avatar.set_visible(visible); - self.header.set_visible(visible); - - if let Some(row) = obj.parent() { - if visible { - row.add_css_class("has-header"); - } else { - row.remove_css_class("has-header"); - } - } - - obj.notify_show_header(); - } - /// Set the event that is presented. fn set_event(&self, event: Event) { let obj = self.obj(); @@ -180,21 +154,22 @@ mod imp { .sync_create() .build(); - let show_header_binding = event - .bind_property("show-header", &*obj, "show-header") - .sync_create() - .build(); - let state_binding = event .bind_property("state", &*self.message_state, "state") .sync_create() .build(); - self.bindings.borrow_mut().append(&mut vec![ - display_name_binding, - show_header_binding, - state_binding, - ]); + self.bindings + .borrow_mut() + .append(&mut vec![display_name_binding, state_binding]); + + let header_state_handler = event.connect_header_state_notify(clone!( + #[weak(rename_to = imp)] + self, + move |_| { + imp.update_header(); + } + )); let timestamp_handler = event.connect_timestamp_notify(clone!( #[weak(rename_to = imp)] @@ -217,14 +192,44 @@ mod imp { self.reactions .set_reaction_list(&event.room().get_or_create_members(), &event.reactions()); self.read_receipts.set_source(event.read_receipts()); - self.event - .set(event, vec![timestamp_handler, item_changed_handler]); + self.event.set( + event, + vec![ + header_state_handler, + timestamp_handler, + item_changed_handler, + ], + ); obj.notify_event(); self.update_content(); + self.update_header(); self.update_timestamp(); } + /// Update the header for the current event. + fn update_header(&self) { + let Some(event) = self.event.obj() else { + return; + }; + + let header_state = event.header_state(); + let avatar_name_visible = header_state == EventHeaderState::Full; + let header_visible = header_state != EventHeaderState::Hidden; + + self.avatar.set_visible(avatar_name_visible); + self.display_name.set_visible(avatar_name_visible); + self.header.set_visible(header_visible); + + if let Some(row) = self.obj().parent() { + if avatar_name_visible { + row.add_css_class("has-avatar"); + } else { + row.remove_css_class("has-avatar"); + } + } + } + /// Update the displayed timestamp for the current event with the /// current clock format setting. fn update_timestamp(&self) { diff --git a/src/session/view/content/room_history/message_row/mod.ui b/src/session/view/content/room_history/message_row/mod.ui index f46050b6..39abb1b4 100644 --- a/src/session/view/content/room_history/message_row/mod.ui +++ b/src/session/view/content/room_history/message_row/mod.ui @@ -39,6 +39,8 @@ + true + end