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(); @include vendor.focus-ring();
&.has-header { &.has-avatar {
margin-top: 6px; margin-top: 6px;
} }
&:not(.has-header) { &:not(.has-avatar) {
.event-content { .event-content {
&:dir(ltr) { &:dir(ltr) {
margin-left: 54px; margin-left: 54px;
@ -206,7 +206,7 @@ sender-avatar {
} }
state-group-row.room-history-row { state-group-row.room-history-row {
&:not(.has-header) { &:not(.has-avatar) {
.event-content { .event-content {
&:dir(ltr) { &:dir(ltr) {
margin-left: 42px; 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_group;
mod reaction_list; mod reaction_list;
pub use self::{ pub(crate) use self::{
reaction_group::{ReactionData, ReactionGroup}, reaction_group::{ReactionData, ReactionGroup},
reaction_list::ReactionList, reaction_list::ReactionList,
}; };
@ -53,11 +53,25 @@ pub enum MessageState {
/// The read receipt of a user. /// The read receipt of a user.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct UserReadReceipt { pub(crate) struct UserReadReceipt {
/// The ID of the user. /// The ID of the user.
pub user_id: OwnedUserId, pub(crate) user_id: OwnedUserId,
/// The data of the receipt. /// 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 { mod imp {
@ -134,9 +148,9 @@ mod imp {
/// Whether this event has any read receipt. /// Whether this event has any read receipt.
#[property(get = Self::has_read_receipts)] #[property(get = Self::has_read_receipts)]
has_read_receipts: PhantomData<bool>, has_read_receipts: PhantomData<bool>,
/// Whether this event should show its header in the room history. /// The state of the header of the event in the room history.
#[property(get, set = Self::set_show_header, explicit_notify)] #[property(get, set = Self::set_header_state, explicit_notify, builder(EventHeaderState::default()))]
show_header: Cell<bool>, header_state: Cell<EventHeaderState>,
} }
#[glib::object_subclass] #[glib::object_subclass]
@ -462,14 +476,14 @@ mod imp {
self.read_receipts().n_items() > 0 self.read_receipts().n_items() > 0
} }
/// Set whether this event should show its header in the room history. /// Set the state of the header of the event in the room history.
fn set_show_header(&self, show: bool) { fn set_header_state(&self, state: EventHeaderState) {
if self.show_header.get() == show { if self.header_state.get() == state {
return; return;
} }
self.show_header.set(show); self.header_state.set(state);
self.obj().notify_show_header(); 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(); let current_sender = current.sender_id();
if !current.can_show_header() { if !current.can_show_header() {
current.set_show_header(false); current.set_header_state(EventHeaderState::Hidden);
previous_sender = None; previous_sender = None;
previous_timestamp = None; previous_timestamp = None;
continue; continue;
} }
let show_header = if previous_sender let header_state = if previous_sender
.as_ref() .as_ref()
.is_none_or(|previous_sender| current_sender != *previous_sender) .is_none_or(|previous_sender| current_sender != *previous_sender)
{ {
// The sender is different, show header. // The sender is different, show the full header.
true EventHeaderState::Full
} else if previous_timestamp } else if previous_timestamp
.and_then(|ts| current.origin_server_ts().0.checked_sub(ts.0)) .and_then(|ts| current.origin_server_ts().0.checked_sub(ts.0))
.is_some_and(|elapsed| u64::from(elapsed) >= MAX_TIME_BETWEEN_HEADERS) .is_some_and(|elapsed| u64::from(elapsed) >= MAX_TIME_BETWEEN_HEADERS)
{ {
// Too much time has passed, show header. // Too much time has passed, show the timestamp.
true EventHeaderState::TimestampOnly
} else { } else {
// Do not show header. // 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_sender = Some(current_sender);
previous_timestamp = Some(current.origin_server_ts()); 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. /// Set the event presented by this row.
fn set_event(&self, event: Option<Event>) { fn set_event(&self, event: Option<Event>) {
// Reinitialize the header. // Reinitialize the header.
self.obj().remove_css_class("has-header"); self.obj().remove_css_class("has-avatar");
self.disconnect_event_signals(); 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 self::{message_state_stack::MessageStateStack, reaction_list::MessageReactionList};
use super::{ReadReceiptsList, SenderAvatar}; use super::{ReadReceiptsList, SenderAvatar};
use crate::{ 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, Application, Window,
}; };
@ -56,11 +60,6 @@ mod imp {
/// The event that is presented. /// The event that is presented.
#[property(get, set = Self::set_event, explicit_notify)] #[property(get, set = Self::set_event, explicit_notify)]
event: BoundObject<Event>, 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 /// The texture of the image preview displayed by the descendant of this
/// widget, if any. /// widget, if any.
#[property(get = Self::texture)] #[property(get = Self::texture)]
@ -137,31 +136,6 @@ mod imp {
impl BinImpl for MessageRow {} impl BinImpl for MessageRow {}
impl 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. /// Set the event that is presented.
fn set_event(&self, event: Event) { fn set_event(&self, event: Event) {
let obj = self.obj(); let obj = self.obj();
@ -180,21 +154,22 @@ mod imp {
.sync_create() .sync_create()
.build(); .build();
let show_header_binding = event
.bind_property("show-header", &*obj, "show-header")
.sync_create()
.build();
let state_binding = event let state_binding = event
.bind_property("state", &*self.message_state, "state") .bind_property("state", &*self.message_state, "state")
.sync_create() .sync_create()
.build(); .build();
self.bindings.borrow_mut().append(&mut vec![ self.bindings
display_name_binding, .borrow_mut()
show_header_binding, .append(&mut vec![display_name_binding, state_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!( let timestamp_handler = event.connect_timestamp_notify(clone!(
#[weak(rename_to = imp)] #[weak(rename_to = imp)]
@ -217,14 +192,44 @@ mod imp {
self.reactions self.reactions
.set_reaction_list(&event.room().get_or_create_members(), &event.reactions()); .set_reaction_list(&event.room().get_or_create_members(), &event.reactions());
self.read_receipts.set_source(event.read_receipts()); self.read_receipts.set_source(event.read_receipts());
self.event self.event.set(
.set(event, vec![timestamp_handler, item_changed_handler]); event,
vec![
header_state_handler,
timestamp_handler,
item_changed_handler,
],
);
obj.notify_event(); obj.notify_event();
self.update_content(); self.update_content();
self.update_header();
self.update_timestamp(); 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 /// Update the displayed timestamp for the current event with the
/// current clock format setting. /// current clock format setting.
fn update_timestamp(&self) { fn update_timestamp(&self) {

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

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

Loading…
Cancel
Save