[fractal/fractal-next] verification: Add InfoBar for user verifications
- From: Julian Sparber <jsparber src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [fractal/fractal-next] verification: Add InfoBar for user verifications
- Date: Thu, 13 Jan 2022 14:34:40 +0000 (UTC)
commit 1d8de86b553525cdefc058350fb50c9230b1658b
Author: Julian Sparber <julian sparber net>
Date: Fri Jan 7 19:01:44 2022 +0100
verification: Add InfoBar for user verifications
data/resources/resources.gresource.xml | 1 +
data/resources/ui/content-room-history.ui | 12 ++
data/resources/ui/content-verification-info-bar.ui | 43 +++++
po/POTFILES.in | 1 +
src/meson.build | 1 +
src/session/content/room_history/mod.rs | 6 +-
.../content/room_history/verification_info_bar.rs | 177 +++++++++++++++++++++
src/session/mod.rs | 5 +
8 files changed, 245 insertions(+), 1 deletion(-)
---
diff --git a/data/resources/resources.gresource.xml b/data/resources/resources.gresource.xml
index a1600b22..ffb2867d 100644
--- a/data/resources/resources.gresource.xml
+++ b/data/resources/resources.gresource.xml
@@ -26,6 +26,7 @@
<file compressed="true" preprocess="xml-stripblanks"
alias="content-state-creation.ui">ui/content-state-creation.ui</file>
<file compressed="true" preprocess="xml-stripblanks"
alias="content-markdown-popover.ui">ui/content-markdown-popover.ui</file>
<file compressed="true" preprocess="xml-stripblanks"
alias="content-invite.ui">ui/content-invite.ui</file>
+ <file compressed="true" preprocess="xml-stripblanks"
alias="content-verification-info-bar.ui">ui/content-verification-info-bar.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="event-menu.ui">ui/event-menu.ui</file>
<file compressed="true" preprocess="xml-stripblanks"
alias="event-source-dialog.ui">ui/event-source-dialog.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="login.ui">ui/login.ui</file>
diff --git a/data/resources/ui/content-room-history.ui b/data/resources/ui/content-room-history.ui
index dd5454f2..473ead9c 100644
--- a/data/resources/ui/content-room-history.ui
+++ b/data/resources/ui/content-room-history.ui
@@ -82,6 +82,17 @@
</property>
</object>
</child>
+ <child>
+ <object class="ContentVerificationInfoBar" id="verification_info_bar">
+ <binding name="request">
+ <lookup name="verification" type="Timeline">
+ <lookup name="timeline" type="Room">
+ <lookup name="room">ContentRoomHistory</lookup>
+ </lookup>
+ </lookup>
+ </binding>
+ </object>
+ </child>
<child>
<object class="GtkStack" id="stack">
<property name="transition-type">crossfade</property>
@@ -225,3 +236,4 @@
</child>
</template>
</interface>
+
diff --git a/data/resources/ui/content-verification-info-bar.ui
b/data/resources/ui/content-verification-info-bar.ui
new file mode 100644
index 00000000..79bd3a67
--- /dev/null
+++ b/data/resources/ui/content-verification-info-bar.ui
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="ContentVerificationInfoBar" parent="AdwBin">
+ <style>
+ <class name="info"/>
+ </style>
+ <child>
+ <object class="GtkRevealer" id="revealer">
+ <property name="child">
+ <object class="GtkBox">
+ <child>
+ <object class="GtkLabel" id="label">
+ <property name="margin-start">12</property>
+ <property name="hexpand">True</property>
+ <property name="halign">start</property>
+ <property name="wrap">True</property>
+ <property name="wrap-mode">word-char</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="button">
+ <property name="action-name">verification.accept</property>
+ <property name="valign">center</property>
+ <style>
+ <class name="suggested-action"/>
+ </style>
+ </object>
+ </child>
+ <child type="end">
+ <object class="GtkButton">
+ <property name="margin-end">12</property>
+ <property name="label" translatable="yes">Decline</property>
+ <property name="action-name">verification.decline</property>
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </template>
+</interface>
+
diff --git a/po/POTFILES.in b/po/POTFILES.in
index a4c16ada..5d48afc1 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -43,6 +43,7 @@ src/session/content/room_history/message_row/media.rs
src/session/content/room_history/message_row/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/verification_info_bar.rs
src/session/content/verification/identity_verification_widget.rs
src/session/content/verification/session_verification.rs
src/session/media_viewer.rs
diff --git a/src/meson.build b/src/meson.build
index 794f42c6..97cafe3b 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -71,6 +71,7 @@ sources = files(
'session/content/room_history/state_row/creation.rs',
'session/content/room_history/state_row/mod.rs',
'session/content/room_history/state_row/tombstone.rs',
+ 'session/content/room_history/verification_info_bar.rs',
'session/content/mod.rs',
'session/content/room_details/invite_subpage/invitee.rs',
'session/content/room_details/invite_subpage/mod.rs',
diff --git a/src/session/content/room_history/mod.rs b/src/session/content/room_history/mod.rs
index c38f481f..97ed176e 100644
--- a/src/session/content/room_history/mod.rs
+++ b/src/session/content/room_history/mod.rs
@@ -2,10 +2,12 @@ mod divider_row;
mod item_row;
mod message_row;
mod state_row;
+mod verification_info_bar;
use self::divider_row::DividerRow;
use self::item_row::ItemRow;
use self::state_row::StateRow;
+use self::verification_info_bar::VerificationInfoBar;
use adw::subclass::prelude::*;
use gtk::{
@@ -20,7 +22,7 @@ use sourceview::prelude::*;
use crate::components::{CustomEntry, Pill, RoomTitle};
use crate::session::content::{MarkdownPopover, RoomDetails};
-use crate::session::room::{Item, Room, RoomType};
+use crate::session::room::{Item, Room, RoomType, Timeline};
use crate::session::user::UserExt;
mod imp {
@@ -76,6 +78,8 @@ mod imp {
CustomEntry::static_type();
ItemRow::static_type();
MarkdownPopover::static_type();
+ VerificationInfoBar::static_type();
+ Timeline::static_type();
Self::bind_template(klass);
klass.set_accessible_role(gtk::AccessibleRole::Group);
klass.install_action(
diff --git a/src/session/content/room_history/verification_info_bar.rs
b/src/session/content/room_history/verification_info_bar.rs
new file mode 100644
index 00000000..212ddd36
--- /dev/null
+++ b/src/session/content/room_history/verification_info_bar.rs
@@ -0,0 +1,177 @@
+use adw::subclass::prelude::*;
+use gtk::{glib, glib::clone, prelude::*, subclass::prelude::*, CompositeTemplate};
+
+use crate::session::user::UserExt;
+use crate::session::verification::{IdentityVerification, VerificationMode};
+use gettextrs::gettext;
+mod imp {
+ use super::*;
+ use glib::subclass::InitializingObject;
+ use glib::SignalHandlerId;
+ use std::cell::RefCell;
+
+ #[derive(Debug, Default, CompositeTemplate)]
+ #[template(resource = "/org/gnome/FractalNext/content-verification-info-bar.ui")]
+ pub struct VerificationInfoBar {
+ #[template_child]
+ pub revealer: TemplateChild<gtk::Revealer>,
+ #[template_child]
+ pub label: TemplateChild<gtk::Label>,
+ #[template_child]
+ pub button: TemplateChild<gtk::Button>,
+ pub request: RefCell<Option<IdentityVerification>>,
+ pub mode_handler: RefCell<Option<SignalHandlerId>>,
+ pub user_handler: RefCell<Option<SignalHandlerId>>,
+ }
+
+ #[glib::object_subclass]
+ impl ObjectSubclass for VerificationInfoBar {
+ const NAME: &'static str = "ContentVerificationInfoBar";
+ type Type = super::VerificationInfoBar;
+ type ParentType = adw::Bin;
+
+ fn class_init(klass: &mut Self::Class) {
+ klass.set_css_name("infobar");
+ Self::bind_template(klass);
+
+ klass.set_accessible_role(gtk::AccessibleRole::Group);
+
+ klass.install_action("verification.accept", None, move |widget, _, _| {
+ let request = widget.request().unwrap();
+ request.accept();
+ request.session().select_item(Some(request.upcast()));
+ });
+
+ klass.install_action("verification.decline", None, move |widget, _, _| {
+ widget.request().unwrap().cancel();
+ });
+ }
+
+ fn instance_init(obj: &InitializingObject<Self>) {
+ obj.init_template();
+ }
+ }
+
+ impl ObjectImpl for VerificationInfoBar {
+ fn properties() -> &'static [glib::ParamSpec] {
+ use once_cell::sync::Lazy;
+ static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
+ vec![glib::ParamSpec::new_object(
+ "request",
+ "Request",
+ "The verification request this InfoBar is showing",
+ IdentityVerification::static_type(),
+ glib::ParamFlags::READWRITE,
+ )]
+ });
+
+ PROPERTIES.as_ref()
+ }
+
+ fn set_property(
+ &self,
+ obj: &Self::Type,
+ _id: usize,
+ value: &glib::Value,
+ pspec: &glib::ParamSpec,
+ ) {
+ match pspec.name() {
+ "request" => obj.set_request(value.get().unwrap()),
+ _ => unimplemented!(),
+ }
+ }
+
+ fn property(&self, obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
+ match pspec.name() {
+ "request" => obj.request().to_value(),
+ _ => unimplemented!(),
+ }
+ }
+ }
+ impl WidgetImpl for VerificationInfoBar {}
+ impl BinImpl for VerificationInfoBar {}
+}
+
+glib::wrapper! {
+ pub struct VerificationInfoBar(ObjectSubclass<imp::VerificationInfoBar>)
+ @extends gtk::Widget, adw::Bin, @implements gtk::Accessible;
+}
+
+impl VerificationInfoBar {
+ pub fn new(label: String) -> Self {
+ glib::Object::new(&[("label", &label)]).expect("Failed to create VerificationInfoBar")
+ }
+
+ pub fn request(&self) -> Option<IdentityVerification> {
+ let priv_ = imp::VerificationInfoBar::from_instance(self);
+ priv_.request.borrow().clone()
+ }
+
+ pub fn set_request(&self, request: Option<IdentityVerification>) {
+ let priv_ = imp::VerificationInfoBar::from_instance(self);
+
+ if let Some(old_request) = &*priv_.request.borrow() {
+ if Some(old_request) == request.as_ref() {
+ return;
+ }
+
+ if let Some(handler) = priv_.mode_handler.take() {
+ old_request.disconnect(handler);
+ }
+
+ if let Some(handler) = priv_.user_handler.take() {
+ old_request.user().disconnect(handler);
+ }
+ }
+
+ if let Some(ref request) = request {
+ let handler = request.connect_notify_local(
+ Some("mode"),
+ clone!(@weak self as obj => move |_, _| {
+ obj.update_view();
+ }),
+ );
+
+ priv_.mode_handler.replace(Some(handler));
+
+ let handler = request.user().connect_notify_local(
+ Some("display-name"),
+ clone!(@weak self as obj => move |_, _| {
+ obj.update_view();
+ }),
+ );
+
+ priv_.user_handler.replace(Some(handler));
+ }
+
+ priv_.request.replace(request);
+
+ self.update_view();
+ self.notify("request");
+ }
+
+ pub fn update_view(&self) {
+ let priv_ = imp::VerificationInfoBar::from_instance(self);
+ let visible = if let Some(request) = self.request() {
+ if request.is_finished() {
+ false
+ } else if matches!(request.mode(), VerificationMode::Requested) {
+ // Translators: The value is the display name of the user who wants to be verified
+ priv_.label.set_markup(&gettext!(
+ "<b>{}</b> wants to be verified",
+ request.user().display_name()
+ ));
+ priv_.button.set_label(&gettext("Verify"));
+ true
+ } else {
+ priv_.label.set_label(&gettext("Verification in progess"));
+ priv_.button.set_label(&gettext("Continue"));
+ true
+ }
+ } else {
+ false
+ };
+
+ priv_.revealer.set_reveal_child(visible);
+ }
+}
diff --git a/src/session/mod.rs b/src/session/mod.rs
index 78abb300..9c848b8e 100644
--- a/src/session/mod.rs
+++ b/src/session/mod.rs
@@ -270,6 +270,11 @@ impl Session {
.set_selected_item(room.map(|item| item.upcast()));
}
+ pub fn select_item(&self, item: Option<glib::Object>) {
+ let priv_ = imp::Session::from_instance(self);
+ priv_.sidebar.set_selected_item(item);
+ }
+
pub fn select_room_by_id(&self, room_id: RoomId) {
if let Some(room) = self.room_list().get(&room_id) {
self.select_room(Some(room));
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]