Browse Source

room-history: Only show timestamp when too much time has passed between events

Instead of the full header with name and avatar
merge-requests/2003/head
Kévin Commaille 11 months ago
parent
commit
b7b309f1d4
No known key found for this signature in database
GPG Key ID: C971D9DBC9D678D
  1. 6
      data/resources/stylesheet/_room_history.scss
  2. 38
      src/session/model/room/timeline/event/mod.rs
  3. 16
      src/session/model/room/timeline/mod.rs
  4. 2
      src/session/view/content/room_history/event_row.rs
  5. 91
      src/session/view/content/room_history/message_row/mod.rs
  6. 2
      src/session/view/content/room_history/message_row/mod.ui

6
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;

38
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<bool>,
/// Whether this event should show its header in the room history.
#[property(get, set = Self::set_show_header, explicit_notify)]
show_header: Cell<bool>,
/// 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<EventHeaderState>,
}
#[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();
}
}
}

16
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());
}

2
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<Event>) {
// Reinitialize the header.
self.obj().remove_css_class("has-header");
self.obj().remove_css_class("has-avatar");
self.disconnect_event_signals();

91
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<Event>,
/// 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<bool>,
/// 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) {

2
src/session/view/content/room_history/message_row/mod.ui

@ -39,6 +39,8 @@
</child>
<child>
<object class="GtkLabel" id="timestamp">
<property name="hexpand">true</property>
<property name="halign">end</property>
<style>
<class name="caption"/>
<class name="timestamp"/>

Loading…
Cancel
Save