message-row: Show the sending status of messages
Also logs if a sending error is encountered
This commit is contained in:
parent
b1de0cee42
commit
71611bc34e
10 changed files with 367 additions and 55 deletions
2
data/resources/icons/scalable/status/done-symbolic.svg
Normal file
2
data/resources/icons/scalable/status/done-symbolic.svg
Normal file
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="16px" viewBox="0 0 16 16" width="16px"><filter id="a" height="100%" width="100%" x="0%" y="0%"><feColorMatrix color-interpolation-filters="sRGB" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/></filter><mask id="b"><g filter="url(#a)"><path d="m -1.6 -1.6 h 19.2 v 19.2 h -19.2 z" fill-opacity="0.5"/></g></mask><clipPath id="c"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="d"><g filter="url(#a)"><path d="m -1.6 -1.6 h 19.2 v 19.2 h -19.2 z" fill-opacity="0.7"/></g></mask><clipPath id="e"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="f"><g filter="url(#a)"><path d="m -1.6 -1.6 h 19.2 v 19.2 h -19.2 z" fill-opacity="0.35"/></g></mask><clipPath id="g"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><g mask="url(#b)"><g clip-path="url(#c)" transform="matrix(1 0 0 1 -160 -80)"><path d="m 550 182 c -0.351562 0.003906 -0.695312 0.101562 -1 0.28125 v 3.4375 c 0.304688 0.179688 0.648438 0.277344 1 0.28125 c 1.105469 0 2 -0.894531 2 -2 s -0.894531 -2 -2 -2 z m 0 5 c -0.339844 0 -0.679688 0.058594 -1 0.175781 v 6.824219 h 4 v -4 c 0 -1.65625 -1.34375 -3 -3 -3 z m 0 0"/></g></g><g mask="url(#d)"><g clip-path="url(#e)" transform="matrix(1 0 0 1 -160 -80)"><path d="m 569 182 v 4 c 1.105469 0 2 -0.894531 2 -2 s -0.894531 -2 -2 -2 z m 0 5 v 7 h 3 v -4 c 0 -1.65625 -1.34375 -3 -3 -3 z m 0 0"/></g></g><g mask="url(#f)"><g clip-path="url(#g)" transform="matrix(1 0 0 1 -160 -80)"><path d="m 573 182.269531 v 3.449219 c 0.613281 -0.355469 0.996094 -1.007812 1 -1.71875 c 0 -0.714844 -0.382812 -1.375 -1 -1.730469 z m 0 4.90625 v 6.824219 h 2 v -4 c 0 -1.269531 -0.800781 -2.402344 -2 -2.824219 z m 0 0"/></g></g><path d="m 8 0 c -4.417969 0 -8 3.582031 -8 8 s 3.582031 8 8 8 s 8 -3.582031 8 -8 s -3.582031 -8 -8 -8 z m 3.164062 5.859375 c 0.640626 0.046875 0.933594 0.824219 0.476563 1.28125 l -3.640625 3.640625 c -0.292969 0.292969 -0.769531 0.292969 -1.0625 0 l -2.175781 -2.109375 c -0.707031 -0.710937 0.355469 -1.773437 1.0625 -1.0625 l 1.644531 1.578125 l 3.109375 -3.109375 c 0.15625 -0.152344 0.367187 -0.234375 0.585937 -0.21875 z m 0 0" fill="#2e3436"/></svg>
|
After Width: | Height: | Size: 2.2 KiB |
|
@ -43,6 +43,7 @@
|
|||
<file preprocess="xml-stripblanks">icons/scalable/status/checkmark-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/devices-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/document-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/done-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/empty-page-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/error-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/explore-symbolic.svg</file>
|
||||
|
|
|
@ -84,7 +84,8 @@ src/session/view/content/room_history/message_row/content.rs
|
|||
src/session/view/content/room_history/message_row/file.ui
|
||||
src/session/view/content/room_history/message_row/location.rs
|
||||
src/session/view/content/room_history/message_row/media.rs
|
||||
src/session/view/content/room_history/message_row/mod.ui
|
||||
src/session/view/content/room_history/message_row/message_state_stack.rs
|
||||
src/session/view/content/room_history/message_row/message_state_stack.ui
|
||||
src/session/view/content/room_history/message_toolbar/attachment_dialog.ui
|
||||
src/session/view/content/room_history/message_toolbar/mod.rs
|
||||
src/session/view/content/room_history/message_toolbar/mod.ui
|
||||
|
|
|
@ -12,10 +12,10 @@ pub use self::{
|
|||
avatar::{AvatarData, AvatarImage, AvatarUriSource},
|
||||
notifications::Notifications,
|
||||
room::{
|
||||
Event, EventKey, HighlightFlags, Member, MemberList, MemberRole, Membership, PowerLevel,
|
||||
ReactionGroup, ReactionList, Room, RoomType, Timeline, TimelineItem, TimelineItemExt,
|
||||
TimelineState, TypingList, UserReadReceipt, VirtualItem, VirtualItemKind, POWER_LEVEL_MAX,
|
||||
POWER_LEVEL_MIN,
|
||||
Event, EventKey, HighlightFlags, Member, MemberList, MemberRole, Membership, MessageState,
|
||||
PowerLevel, ReactionGroup, ReactionList, Room, RoomType, Timeline, TimelineItem,
|
||||
TimelineItemExt, TimelineState, TypingList, UserReadReceipt, VirtualItem, VirtualItemKind,
|
||||
POWER_LEVEL_MAX, POWER_LEVEL_MIN,
|
||||
},
|
||||
room_list::RoomList,
|
||||
session::{Session, SessionState},
|
||||
|
|
|
@ -3,14 +3,15 @@ use std::{borrow::Cow, fmt};
|
|||
use gtk::{gio, glib, prelude::*, subclass::prelude::*};
|
||||
use indexmap::IndexMap;
|
||||
use matrix_sdk_ui::timeline::{
|
||||
AnyOtherFullStateEventContent, Error as TimelineError, EventTimelineItem, RepliedToEvent,
|
||||
TimelineDetails, TimelineItemContent,
|
||||
AnyOtherFullStateEventContent, Error as TimelineError, EventSendState, EventTimelineItem,
|
||||
RepliedToEvent, TimelineDetails, TimelineItemContent,
|
||||
};
|
||||
use ruma::{
|
||||
events::{receipt::Receipt, room::message::MessageType, AnySyncTimelineEvent},
|
||||
serde::Raw,
|
||||
EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedTransactionId, OwnedUserId,
|
||||
};
|
||||
use tracing::error;
|
||||
|
||||
mod reaction_group;
|
||||
mod reaction_list;
|
||||
|
@ -67,6 +68,18 @@ impl glib::FromVariant for EventKey {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Hash, Eq, PartialEq, Clone, Copy, glib::Enum)]
|
||||
#[repr(u32)]
|
||||
#[enum_type(name = "MessageState")]
|
||||
pub enum MessageState {
|
||||
#[default]
|
||||
None = 0,
|
||||
Sending = 1,
|
||||
Error = 2,
|
||||
Cancelled = 3,
|
||||
Edited = 4,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, glib::Boxed)]
|
||||
#[boxed_type(name = "BoxedEventTimelineItem")]
|
||||
pub struct BoxedEventTimelineItem(EventTimelineItem);
|
||||
|
@ -79,7 +92,7 @@ pub struct UserReadReceipt {
|
|||
}
|
||||
|
||||
mod imp {
|
||||
use std::cell::RefCell;
|
||||
use std::cell::{Cell, RefCell};
|
||||
|
||||
use glib::object::WeakRef;
|
||||
use once_cell::sync::Lazy;
|
||||
|
@ -99,6 +112,9 @@ mod imp {
|
|||
|
||||
/// The read receipts on this event.
|
||||
pub read_receipts: gio::ListStore,
|
||||
|
||||
/// The state of this event.
|
||||
pub state: Cell<MessageState>,
|
||||
}
|
||||
|
||||
impl Default for Event {
|
||||
|
@ -108,6 +124,7 @@ mod imp {
|
|||
room: Default::default(),
|
||||
reactions: Default::default(),
|
||||
read_receipts: gio::ListStore::new::<glib::BoxedAnyObject>(),
|
||||
state: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -146,6 +163,9 @@ mod imp {
|
|||
glib::ParamSpecBoolean::builder("has-read-receipts")
|
||||
.read_only()
|
||||
.build(),
|
||||
glib::ParamSpecEnum::builder::<MessageState>("state")
|
||||
.read_only()
|
||||
.build(),
|
||||
]
|
||||
});
|
||||
|
||||
|
@ -179,6 +199,7 @@ mod imp {
|
|||
"is-highlighted" => obj.is_highlighted().to_value(),
|
||||
"read-receipts" => obj.read_receipts().to_value(),
|
||||
"has-read-receipts" => obj.has_read_receipts().to_value(),
|
||||
"state" => obj.state().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
@ -291,6 +312,7 @@ impl Event {
|
|||
if self.is_highlighted() != was_highlighted {
|
||||
self.notify("is-highlighted");
|
||||
}
|
||||
self.update_state();
|
||||
}
|
||||
|
||||
/// The raw JSON source for this `Event`, if it has been echoed back
|
||||
|
@ -447,6 +469,51 @@ impl Event {
|
|||
}
|
||||
}
|
||||
|
||||
/// The state of this `Event`.
|
||||
pub fn state(&self) -> MessageState {
|
||||
self.imp().state.get()
|
||||
}
|
||||
|
||||
/// Compute the current state of this `Event`.
|
||||
fn compute_state(&self) -> MessageState {
|
||||
let item_ref = self.imp().item.borrow();
|
||||
let Some(item) = item_ref.as_ref() else {
|
||||
return MessageState::None;
|
||||
};
|
||||
|
||||
if let Some(send_state) = item.send_state() {
|
||||
match send_state {
|
||||
EventSendState::NotSentYet => return MessageState::Sending,
|
||||
EventSendState::SendingFailed { error } => {
|
||||
if self.state() != MessageState::Error {
|
||||
error!("Failed to send message: {error}");
|
||||
}
|
||||
|
||||
return MessageState::Error;
|
||||
}
|
||||
EventSendState::Cancelled => return MessageState::Cancelled,
|
||||
EventSendState::Sent { .. } => {}
|
||||
}
|
||||
}
|
||||
|
||||
match item.content() {
|
||||
TimelineItemContent::Message(msg) if msg.is_edited() => MessageState::Edited,
|
||||
_ => MessageState::None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the state of this `Event`.
|
||||
fn update_state(&self) {
|
||||
let state = self.compute_state();
|
||||
|
||||
if self.state() == state {
|
||||
return;
|
||||
}
|
||||
|
||||
self.imp().state.set(state);
|
||||
self.notify("state");
|
||||
}
|
||||
|
||||
/// Whether this `Event` should be highlighted.
|
||||
pub fn is_highlighted(&self) -> bool {
|
||||
let item_ref = self.imp().item.borrow();
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
use adw::subclass::prelude::*;
|
||||
use gettextrs::gettext;
|
||||
use gtk::{glib, glib::clone, prelude::*, CompositeTemplate};
|
||||
|
||||
use crate::session::model::MessageState;
|
||||
|
||||
mod imp {
|
||||
use std::cell::Cell;
|
||||
|
||||
use glib::subclass::InitializingObject;
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default, CompositeTemplate)]
|
||||
#[template(
|
||||
resource = "/org/gnome/Fractal/ui/session/view/content/room_history/message_row/message_state_stack.ui"
|
||||
)]
|
||||
pub struct MessageStateStack {
|
||||
/// The state that is currently displayed.
|
||||
pub state: Cell<MessageState>,
|
||||
#[template_child]
|
||||
pub stack: TemplateChild<gtk::Stack>,
|
||||
#[template_child]
|
||||
pub error_image: TemplateChild<gtk::Image>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for MessageStateStack {
|
||||
const NAME: &'static str = "MessageStateStack";
|
||||
type Type = super::MessageStateStack;
|
||||
type ParentType = adw::Bin;
|
||||
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
Self::bind_template(klass);
|
||||
}
|
||||
|
||||
fn instance_init(obj: &InitializingObject<Self>) {
|
||||
obj.init_template();
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for MessageStateStack {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![glib::ParamSpecEnum::builder::<MessageState>("state")
|
||||
.explicit_notify()
|
||||
.build()]
|
||||
});
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
let obj = self.obj();
|
||||
|
||||
match pspec.name() {
|
||||
"state" => {
|
||||
obj.set_state(value.get().unwrap());
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
let obj = self.obj();
|
||||
|
||||
match pspec.name() {
|
||||
"state" => obj.state().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl WidgetImpl for MessageStateStack {}
|
||||
impl BinImpl for MessageStateStack {}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
/// A stack to display the different message states.
|
||||
pub struct MessageStateStack(ObjectSubclass<imp::MessageStateStack>)
|
||||
@extends gtk::Widget, adw::Bin, @implements gtk::Accessible;
|
||||
}
|
||||
|
||||
impl MessageStateStack {
|
||||
/// Create a new `MessageStateStack`.
|
||||
pub fn new() -> Self {
|
||||
glib::Object::new()
|
||||
}
|
||||
|
||||
/// The state that is currently displayed.
|
||||
pub fn state(&self) -> MessageState {
|
||||
self.imp().state.get()
|
||||
}
|
||||
|
||||
/// Set the state to display.
|
||||
pub fn set_state(&self, state: MessageState) {
|
||||
let prev_state = self.state();
|
||||
|
||||
if prev_state == state {
|
||||
return;
|
||||
}
|
||||
|
||||
let imp = self.imp();
|
||||
let stack = &*imp.stack;
|
||||
match state {
|
||||
MessageState::None => {
|
||||
if matches!(
|
||||
prev_state,
|
||||
MessageState::Sending | MessageState::Error | MessageState::Cancelled
|
||||
) {
|
||||
// Show the sent icon for 2 seconds.
|
||||
stack.set_visible_child_name("sent");
|
||||
|
||||
glib::timeout_add_seconds_local_once(
|
||||
2,
|
||||
clone!(@weak self as obj => move || {
|
||||
obj.set_visible(false);
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
self.set_visible(false);
|
||||
}
|
||||
}
|
||||
MessageState::Sending => {
|
||||
stack.set_visible_child_name("sending");
|
||||
self.set_visible(true);
|
||||
}
|
||||
MessageState::Error => {
|
||||
imp.error_image
|
||||
.set_tooltip_text(Some(&gettext("Could not send the message")));
|
||||
stack.set_visible_child_name("error");
|
||||
self.set_visible(true);
|
||||
}
|
||||
MessageState::Cancelled => {
|
||||
imp.error_image
|
||||
.set_tooltip_text(Some(&gettext("An error occurred with the sending queue")));
|
||||
stack.set_visible_child_name("error");
|
||||
self.set_visible(true);
|
||||
}
|
||||
MessageState::Edited => {
|
||||
if matches!(
|
||||
prev_state,
|
||||
MessageState::Sending | MessageState::Error | MessageState::Cancelled
|
||||
) {
|
||||
// Show the sent icon for 2 seconds.
|
||||
stack.set_visible_child_name("sent");
|
||||
|
||||
glib::timeout_add_seconds_local_once(
|
||||
2,
|
||||
clone!(@weak stack => move || {
|
||||
stack.set_visible_child_name("edited");
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
stack.set_visible_child_name("edited");
|
||||
self.set_visible(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
imp.state.set(state);
|
||||
self.notify("state");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="MessageStateStack" parent="AdwBin">
|
||||
<child>
|
||||
<object class="GtkStack" id="stack">
|
||||
<property name="transition-type">crossfade</property>
|
||||
<child>
|
||||
<object class="GtkStackPage">
|
||||
<property name="name">sending</property>
|
||||
<property name="child">
|
||||
<object class="Spinner" id="spinner">
|
||||
<property name="valign">center</property>
|
||||
<!-- Translators: As in 'Sending message…'. -->
|
||||
<property name="tooltip-text" translatable="yes">Sending…</property>
|
||||
<accessibility>
|
||||
<!-- Translators: As in 'Sending message…'. -->
|
||||
<property name="label" translatable="yes">Sending…</property>
|
||||
</accessibility>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkStackPage">
|
||||
<property name="name">error</property>
|
||||
<property name="child">
|
||||
<object class="GtkImage" id="error_image">
|
||||
<property name="valign">center</property>
|
||||
<property name="icon-name">error-symbolic</property>
|
||||
<style>
|
||||
<class name="error"/>
|
||||
</style>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkStackPage">
|
||||
<property name="name">sent</property>
|
||||
<property name="child">
|
||||
<object class="GtkImage">
|
||||
<property name="valign">center</property>
|
||||
<property name="icon-name">done-symbolic</property>
|
||||
<!-- Translators: As in 'Sent message'. -->
|
||||
<property name="tooltip-text" translatable="yes">Sent</property>
|
||||
<accessibility>
|
||||
<!-- Translators: As in 'Sent message'. -->
|
||||
<property name="label" translatable="yes">Sent</property>
|
||||
</accessibility>
|
||||
<style>
|
||||
<class name="success"/>
|
||||
</style>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkStackPage">
|
||||
<property name="name">edited</property>
|
||||
<property name="child">
|
||||
<object class="GtkImage">
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
<property name="icon-name">edit-symbolic</property>
|
||||
<!-- Translators: As in 'Edited message'. -->
|
||||
<property name="tooltip-text" translatable="yes">Edited</property>
|
||||
<accessibility>
|
||||
<!-- Translators: As in 'Edited message'. -->
|
||||
<property name="label" translatable="yes">Edited</property>
|
||||
</accessibility>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
|
@ -3,24 +3,23 @@ mod content;
|
|||
mod file;
|
||||
mod location;
|
||||
mod media;
|
||||
mod message_state_stack;
|
||||
mod reaction;
|
||||
mod reaction_list;
|
||||
mod reply;
|
||||
mod text;
|
||||
|
||||
use adw::{prelude::*, subclass::prelude::*};
|
||||
use gtk::{
|
||||
gdk, glib,
|
||||
glib::{clone, signal::SignalHandlerId},
|
||||
CompositeTemplate,
|
||||
};
|
||||
use gtk::{gdk, glib, glib::clone, CompositeTemplate};
|
||||
use matrix_sdk::ruma::events::room::message::MessageType;
|
||||
use tracing::warn;
|
||||
|
||||
pub use self::content::{ContentFormat, MessageContent};
|
||||
use self::{media::MessageMedia, reaction_list::MessageReactionList};
|
||||
use self::{
|
||||
media::MessageMedia, message_state_stack::MessageStateStack, reaction_list::MessageReactionList,
|
||||
};
|
||||
use super::ReadReceiptsList;
|
||||
use crate::{components::Avatar, prelude::*, session::model::Event, Window};
|
||||
use crate::{components::Avatar, prelude::*, session::model::Event, utils::BoundObject, Window};
|
||||
|
||||
mod imp {
|
||||
use std::cell::RefCell;
|
||||
|
@ -46,12 +45,13 @@ mod imp {
|
|||
#[template_child]
|
||||
pub content: TemplateChild<MessageContent>,
|
||||
#[template_child]
|
||||
pub message_state: TemplateChild<MessageStateStack>,
|
||||
#[template_child]
|
||||
pub reactions: TemplateChild<MessageReactionList>,
|
||||
#[template_child]
|
||||
pub read_receipts: TemplateChild<ReadReceiptsList>,
|
||||
pub source_changed_handler: RefCell<Option<SignalHandlerId>>,
|
||||
pub bindings: RefCell<Vec<glib::Binding>>,
|
||||
pub event: RefCell<Option<Event>>,
|
||||
pub event: BoundObject<Event>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
|
@ -118,6 +118,14 @@ mod imp {
|
|||
),
|
||||
);
|
||||
}
|
||||
|
||||
fn dispose(&self) {
|
||||
self.event.disconnect_signals();
|
||||
|
||||
while let Some(binding) = self.bindings.borrow_mut().pop() {
|
||||
binding.unbind();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for MessageRow {}
|
||||
|
@ -164,20 +172,16 @@ impl MessageRow {
|
|||
}
|
||||
|
||||
pub fn event(&self) -> Option<Event> {
|
||||
self.imp().event.borrow().clone()
|
||||
self.imp().event.obj()
|
||||
}
|
||||
|
||||
pub fn set_event(&self, event: Event) {
|
||||
let imp = self.imp();
|
||||
// Remove signals and bindings from the previous event
|
||||
if let Some(event) = imp.event.take() {
|
||||
if let Some(source_changed_handler) = imp.source_changed_handler.take() {
|
||||
event.disconnect(source_changed_handler);
|
||||
}
|
||||
|
||||
while let Some(binding) = imp.bindings.borrow_mut().pop() {
|
||||
binding.unbind();
|
||||
}
|
||||
// Remove signals and bindings from the previous event.
|
||||
imp.event.disconnect_signals();
|
||||
while let Some(binding) = imp.bindings.borrow_mut().pop() {
|
||||
binding.unbind();
|
||||
}
|
||||
|
||||
imp.avatar
|
||||
|
@ -199,25 +203,30 @@ impl MessageRow {
|
|||
.sync_create()
|
||||
.build();
|
||||
|
||||
let state_binding = event
|
||||
.bind_property("state", &*imp.message_state, "state")
|
||||
.sync_create()
|
||||
.build();
|
||||
|
||||
imp.bindings.borrow_mut().append(&mut vec![
|
||||
display_name_binding,
|
||||
show_header_binding,
|
||||
timestamp_binding,
|
||||
state_binding,
|
||||
]);
|
||||
|
||||
imp.source_changed_handler
|
||||
.replace(Some(event.connect_notify_local(
|
||||
Some("source"),
|
||||
clone!(@weak self as obj => move |event, _| {
|
||||
obj.update_content(event);
|
||||
}),
|
||||
)));
|
||||
let source_handler = event.connect_notify_local(
|
||||
Some("source"),
|
||||
clone!(@weak self as obj => move |event, _| {
|
||||
obj.update_content(event);
|
||||
}),
|
||||
);
|
||||
self.update_content(&event);
|
||||
|
||||
imp.reactions
|
||||
.set_reaction_list(&event.room().get_or_create_members(), event.reactions());
|
||||
imp.read_receipts.set_source(event.read_receipts());
|
||||
imp.event.replace(Some(event));
|
||||
imp.event.set(event, vec![source_handler]);
|
||||
self.notify("event");
|
||||
}
|
||||
|
||||
|
@ -232,12 +241,10 @@ impl MessageRow {
|
|||
|
||||
/// Open the media viewer with the media content of this row.
|
||||
fn show_media(&self) {
|
||||
let imp = self.imp();
|
||||
let Some(window) = self.root().and_downcast::<Window>() else {
|
||||
return;
|
||||
};
|
||||
let borrowed_event = imp.event.borrow();
|
||||
let Some(event) = borrowed_event.as_ref() else {
|
||||
let Some(event) = self.event() else {
|
||||
return;
|
||||
};
|
||||
let Some(message) = event.message() else {
|
||||
|
@ -245,13 +252,17 @@ impl MessageRow {
|
|||
};
|
||||
|
||||
if matches!(message, MessageType::Image(_) | MessageType::Video(_)) {
|
||||
let Some(media_widget) = imp.content.content_widget().and_downcast::<MessageMedia>()
|
||||
let Some(media_widget) = self
|
||||
.imp()
|
||||
.content
|
||||
.content_widget()
|
||||
.and_downcast::<MessageMedia>()
|
||||
else {
|
||||
warn!("Trying to show media of a non-media message");
|
||||
return;
|
||||
};
|
||||
|
||||
window.session_view().show_media(event, &media_widget);
|
||||
window.session_view().show_media(&event, &media_widget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,22 +62,8 @@
|
|||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
<property name="icon-name">edit-symbolic</property>
|
||||
<!-- Translators: As in 'Edited message'. -->
|
||||
<property name="tooltip-text" translatable="yes">Edited</property>
|
||||
<accessibility>
|
||||
<!-- Translators: As in 'Edited message'. -->
|
||||
<property name="label" translatable="yes">Edited</property>
|
||||
</accessibility>
|
||||
<binding name="visible">
|
||||
<lookup name="is-edited" type="RoomEvent">
|
||||
<lookup name="event">ContentMessageRow</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
<object class="MessageStateStack" id="message_state">
|
||||
<property name="visible">false</property>
|
||||
<layout>
|
||||
<property name="column">2</property>
|
||||
<property name="row">1</property>
|
||||
|
|
|
@ -67,6 +67,7 @@
|
|||
<file compressed="true" preprocess="xml-stripblanks">session/view/content/room_history/message_row/file.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks">session/view/content/room_history/message_row/location.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks">session/view/content/room_history/message_row/media.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks">session/view/content/room_history/message_row/message_state_stack.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks">session/view/content/room_history/message_row/mod.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks">session/view/content/room_history/message_row/reaction/mod.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks">session/view/content/room_history/message_row/reaction/reaction_popover.ui</file>
|
||||
|
|
Loading…
Reference in a new issue