room-history: Allow to join or view the successor in a tombstone event

This commit is contained in:
Kévin Commaille 2023-05-02 16:49:05 +02:00 committed by Kévin Commaille
parent af90c33a68
commit d248af475c
4 changed files with 103 additions and 25 deletions

View file

@ -16,7 +16,7 @@
</child>
<child>
<object class="GtkButton" id="new_room_btn">
<property name="label" translatable="yes">View</property>
<signal name="clicked" handler="join_or_view_successor" swapped="yes"/>
</object>
</child>
</object>

View file

@ -81,6 +81,7 @@ src/session/content/room_history/message_row/media.rs
src/session/content/room_history/mod.rs
src/session/content/room_history/state_row/creation.rs
src/session/content/room_history/state_row/mod.rs
src/session/content/room_history/state_row/tombstone.rs
src/session/content/room_history/typing_row.rs
src/session/content/room_history/verification_info_bar.rs
src/session/content/verification/identity_verification_widget.rs

View file

@ -77,7 +77,7 @@ impl StateRow {
self.update_with_profile_change(&profile_change, &event.sender().display_name())
}
TimelineItemContent::OtherState(other_state) => {
self.update_with_other_state(&other_state)
self.update_with_other_state(event, &other_state)
}
_ => unreachable!(),
}
@ -85,7 +85,7 @@ impl StateRow {
self.imp().read_receipts.set_list(event.read_receipts());
}
fn update_with_other_state(&self, other_state: &OtherState) {
fn update_with_other_state(&self, event: &Event, other_state: &OtherState) {
let widget = match other_state.content() {
AnyOtherFullStateEventContent::RoomCreate(content) => {
WidgetType::Creation(StateCreation::new(content))
@ -110,8 +110,8 @@ impl StateRow {
&[("user", display_name)],
))
}
AnyOtherFullStateEventContent::RoomTombstone(content) => {
WidgetType::Tombstone(StateTombstone::new(content))
AnyOtherFullStateEventContent::RoomTombstone(_) => {
WidgetType::Tombstone(StateTombstone::new(&event.room()))
}
_ => {
warn!(

View file

@ -1,10 +1,12 @@
use adw::{prelude::*, subclass::prelude::*};
use gtk::{glib, CompositeTemplate};
use matrix_sdk::ruma::events::room::tombstone::RoomTombstoneEventContent;
use ruma::events::FullStateEventContent;
use gettextrs::gettext;
use gtk::{glib, glib::clone, CompositeTemplate};
use crate::{session::Room, utils::BoundObjectWeakRef};
mod imp {
use glib::subclass::InitializingObject;
use once_cell::sync::Lazy;
use super::*;
@ -13,6 +15,8 @@ mod imp {
pub struct StateTombstone {
#[template_child]
pub new_room_btn: TemplateChild<gtk::Button>,
/// The [`Room`] this event belongs to.
pub room: BoundObjectWeakRef<Room>,
}
#[glib::object_subclass]
@ -23,6 +27,7 @@ mod imp {
fn class_init(klass: &mut Self::Class) {
Self::bind_template(klass);
Self::Type::bind_template_callbacks(klass);
}
fn instance_init(obj: &InitializingObject<Self>) {
@ -30,7 +35,40 @@ mod imp {
}
}
impl ObjectImpl for StateTombstone {}
impl ObjectImpl for StateTombstone {
fn properties() -> &'static [glib::ParamSpec] {
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
vec![glib::ParamSpecObject::builder::<Room>("room")
.construct_only()
.build()]
});
PROPERTIES.as_ref()
}
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
let obj = self.obj();
match pspec.name() {
"room" => obj.set_room(value.get().unwrap()),
_ => unimplemented!(),
}
}
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
let obj = self.obj();
match pspec.name() {
"room" => obj.room().to_value(),
_ => unimplemented!(),
}
}
fn dispose(&self) {
self.room.disconnect_signals();
}
}
impl WidgetImpl for StateTombstone {}
impl BinImpl for StateTombstone {}
}
@ -40,25 +78,64 @@ glib::wrapper! {
@extends gtk::Widget, adw::Bin, @implements gtk::Accessible;
}
#[gtk::template_callbacks]
impl StateTombstone {
pub fn new(event: &FullStateEventContent<RoomTombstoneEventContent>) -> Self {
let obj: Self = glib::Object::new();
obj.set_event(event);
obj
/// Construct a new `StateTombstone` with the given room.
pub fn new(room: &Room) -> Self {
glib::Object::builder().property("room", room).build()
}
fn set_event(&self, event: &FullStateEventContent<RoomTombstoneEventContent>) {
let new_room_btn = &self.imp().new_room_btn;
let btn_visible = match event {
FullStateEventContent::Original { content, .. } => {
new_room_btn.set_detailed_action_name(&format!(
"session.show-room::{}",
content.replacement_room
));
true
}
FullStateEventContent::Redacted(_) => false,
/// Set the room this event belongs to.
fn set_room(&self, room: Room) {
let imp = self.imp();
let successor_handler = room.connect_notify_local(
Some("successor"),
clone!(@weak self as obj => move |room, _| {
obj.imp().new_room_btn.set_visible(room.successor().is_some());
}),
);
imp.new_room_btn.set_visible(room.successor().is_some());
let successor_room_handler = room.connect_notify_local(
Some("successor-room"),
clone!(@weak self as obj => move |room, _| {
obj.update_button_label(room);
}),
);
self.update_button_label(&room);
imp.room
.set(&room, vec![successor_handler, successor_room_handler]);
}
/// The room this event belongs to.
pub fn room(&self) -> Option<Room> {
self.imp().room.obj()
}
/// Update the button of the label.
fn update_button_label(&self, room: &Room) {
let button = &self.imp().new_room_btn;
if room.successor_room().is_some() {
button.set_label(&gettext("View"));
} else {
button.set_label(&gettext("Join"));
}
}
/// Join or view the successor of this event's room.
#[template_callback]
fn join_or_view_successor(&self) {
let Some(room) = self.room() else {
return;
};
new_room_btn.set_visible(btn_visible);
let Some(successor) = room.successor() else {
return;
};
room.session()
.room_list()
.join_or_view(successor.into(), vec![]);
}
}