Browse Source

verification: Add widget to display incoming verification request

merge-requests/1327/merge
Julian Sparber 4 years ago
parent
commit
735bda23f7
  1. 1
      data/resources/resources.gresource.xml
  2. 25
      data/resources/ui/content.ui
  3. 455
      data/resources/ui/incoming-verification.ui
  4. 1
      src/meson.build
  5. 51
      src/session/content/mod.rs
  6. 425
      src/session/verification/incoming_verification.rs
  7. 2
      src/session/verification/mod.rs
  8. 39
      src/session/verification/verification_list.rs

1
data/resources/resources.gresource.xml

@ -44,6 +44,7 @@
<file compressed="true" preprocess="xml-stripblanks" alias="room-creation.ui">ui/room-creation.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="session-verification.ui">ui/session-verification.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="verification-emoji.ui">ui/verification-emoji.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="incoming-verification.ui">ui/incoming-verification.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="qr-code-scanner.ui">ui/qr-code-scanner.ui</file>
<file compressed="true">style.css</file>
<file preprocess="xml-stripblanks">icons/scalable/actions/send-symbolic.svg</file>

25
data/resources/ui/content.ui

@ -54,6 +54,31 @@
<property name="session" bind-source="Content" bind-property="session" bind-flags="sync-create"/>
</object>
</child>
<child>
<object class="GtkBox" id="verification_page">
<property name="orientation">vertical</property>
<child>
<object class="AdwHeaderBar">
<property name="show-start-title-buttons" bind-source="Content" bind-property="compact" bind-flags="sync-create"/>
<child type="start">
<object class="GtkRevealer">
<property name="transition-type">crossfade</property>
<property name="reveal-child" bind-source="Content" bind-property="compact" bind-flags="sync-create"/>
<property name="child">
<object class="GtkButton">
<property name="icon-name">go-previous-symbolic</property>
<property name="action-name">content.go-back</property>
</object>
</property>
</object>
</child>
</object>
</child>
<child>
<object class="IncomingVerification" id="incoming_verification"/>
</child>
</object>
</child>
</object>
</property>
</template>

455
data/resources/ui/incoming-verification.ui

@ -0,0 +1,455 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="IncomingVerification" parent="AdwBin">
<style>
<class name="incoming-verification"/>
</style>
<child>
<object class="GtkStack" id="main_stack">
<property name="transition-type">crossfade</property>
<property name="vexpand">True</property>
<property name="margin-top">24</property>
<property name="margin-bottom">24</property>
<child>
<object class="GtkStackPage">
<property name="name">accept-request</property>
<property name="child">
<object class="AdwClamp">
<property name="maximum-size">400</property>
<property name="tightening-threshold">300</property>
<property name="child">
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="spacing">18</property>
<property name="valign">center</property>
<property name="halign">center</property>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">Login Request From Another Session</property>
<property name="wrap">True</property>
<property name="wrap-mode">word-char</property>
<property name="justify">center</property>
<style>
<class name="title-1"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">Verify the new session with the current session.</property>
<property name="wrap">True</property>
<property name="wrap-mode">word-char</property>
<property name="justify">center</property>
</object>
</child>
<child>
<object class="GtkPicture">
<property name="file">resource:///org/gnome/FractalNext/icons/scalable/status/other-device.svg</property>
</object>
</child>
<child>
<object class="GtkBox">
<property name="halign">center</property>
<property name="spacing">24</property>
<child>
<object class="GtkButton" id="dismiss_btn">
<property name="label" translatable="yes">Dismiss</property>
<property name="halign">center</property>
<property name="action-name">verification.dismiss</property>
<style>
<class name="pill"/>
</style>
</object>
</child>
<child>
<object class="SpinnerButton" id="accept_btn">
<property name="label" translatable="yes">Accept</property>
<property name="halign">center</property>
<style>
<class name="suggested-action"/>
<class name="pill"/>
</style>
</object>
</child>
</object>
</child>
</object>
</property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">scan-qr-code</property>
<property name="child">
<object class="AdwClamp">
<property name="maximum-size">400</property>
<property name="tightening-threshold">300</property>
<property name="child">
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="spacing">18</property>
<property name="valign">center</property>
<property name="halign">center</property>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">Verify Session</property>
<property name="wrap">True</property>
<property name="wrap-mode">word-char</property>
<property name="justify">center</property>
<style>
<class name="title-1"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">Scan the Qr code with this session from another session logged into this account</property>
<property name="wrap">True</property>
<property name="wrap-mode">word-char</property>
<property name="justify">center</property>
</object>
</child>
<child>
<object class="QrCodeScanner" id="qr_code_scanner">
<property name="margin-top">24</property>
<property name="margin-bottom">24</property>
<property name="vexpand">True</property>
<property name="hexpand">True</property>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">Can't scan QR code?</property>
<property name="wrap">True</property>
<property name="wrap-mode">word-char</property>
<property name="justify">center</property>
</object>
</child>
<child>
<object class="SpinnerButton" id="start_emoji_btn2">
<property name="label" translatable="yes">Compare Emoji</property>
<property name="halign">center</property>
<style>
<class name="pill"/>
</style>
</object>
</child>
<child>
<object class="SpinnerButton" id="take_screenshot_btn2">
<property name="label" translatable="yes">Take a Screenshot of a Qr Code</property>
<property name="halign">center</property>
<style>
<class name="pill"/>
</style>
</object>
</child>
</object>
</property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">qr-code-scanned</property>
<property name="child">
<object class="AdwClamp">
<property name="maximum-size">400</property>
<property name="tightening-threshold">300</property>
<property name="child">
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="spacing">18</property>
<property name="valign">center</property>
<property name="halign">center</property>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">Scan Complete</property>
<property name="wrap">True</property>
<property name="wrap-mode">word-char</property>
<property name="justify">center</property>
<style>
<class name="title-1"/>
</style>
</object>
</child>
<child>
<object class="GtkPicture">
<property name="file">resource:///org/gnome/FractalNext/icons/scalable/status/setup-complete.svg</property>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">You scanned to qr code successfully. You may need to confirm the verification in the other session.</property>
<property name="wrap">True</property>
<property name="wrap-mode">word-char</property>
<property name="justify">center</property>
</object>
</child>
</object>
</property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">no-camera</property>
<property name="child">
<object class="AdwClamp">
<property name="maximum-size">400</property>
<property name="tightening-threshold">300</property>
<property name="child">
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="spacing">18</property>
<property name="valign">center</property>
<property name="halign">center</property>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">Verify Session</property>
<property name="wrap">True</property>
<property name="wrap-mode">word-char</property>
<property name="justify">center</property>
<style>
<class name="title-1"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">Select an option to verify the new session.</property>
<property name="wrap">True</property>
<property name="wrap-mode">word-char</property>
<property name="justify">center</property>
</object>
</child>
<child>
<object class="SpinnerButton" id="start_emoji_btn3">
<property name="label" translatable="yes">Compare Emoji</property>
<property name="halign">center</property>
<style>
<class name="pill"/>
</style>
</object>
</child>
<child>
<object class="SpinnerButton" id="take_screenshot_btn3">
<property name="label" translatable="yes">Take a Screenshot of a Qr Code</property>
<property name="halign">center</property>
<style>
<class name="pill"/>
</style>
</object>
</child>
</object>
</property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">qrcode</property>
<property name="child">
<object class="AdwClamp">
<property name="maximum-size">400</property>
<property name="tightening-threshold">300</property>
<property name="child">
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="spacing">18</property>
<property name="valign">center</property>
<property name="halign">center</property>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">Verify Session</property>
<property name="wrap">True</property>
<property name="wrap-mode">word-char</property>
<property name="justify">center</property>
<style>
<class name="title-1"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">Scan this qr code with the newly logged in session.</property>
<property name="wrap">True</property>
<property name="wrap-mode">word-char</property>
<property name="justify">center</property>
</object>
</child>
<child>
<object class="TriQRCode" id="qrcode">
<property name="valign">center</property>
<property name="halign">center</property>
<property name="margin-top">24</property>
<property name="margin-bottom">24</property>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">Can't scan QR code?</property>
<property name="wrap">True</property>
<property name="wrap-mode">word-char</property>
<property name="justify">center</property>
</object>
</child>
<child>
<object class="SpinnerButton" id="scan_qr_code_btn">
<property name="halign">center</property>
<style>
<class name="pill"/>
</style>
</object>
</child>
<child>
<object class="SpinnerButton" id="start_emoji_btn">
<property name="label" translatable="yes">Compare Emoji</property>
<property name="halign">center</property>
<style>
<class name="pill"/>
</style>
</object>
</child>
</object>
</property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">emoji</property>
<property name="child">
<object class="AdwClamp">
<property name="maximum-size">400</property>
<property name="tightening-threshold">300</property>
<property name="child">
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="spacing">18</property>
<property name="valign">center</property>
<property name="halign">center</property>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">Verify Session</property>
<property name="wrap">True</property>
<property name="wrap-mode">word-char</property>
<property name="justify">center</property>
<style>
<class name="title-1"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">Check if the same emoji appear in the same order on the other device</property>
<property name="wrap">True</property>
<property name="wrap-mode">word-char</property>
<property name="justify">center</property>
</object>
</child>
<child>
<object class="GtkBox" id="emoji_row_1">
<property name="valign">center</property>
<property name="halign">center</property>
<property name="spacing">30</property>
<property name="margin-top">24</property>
</object>
</child>
<child>
<object class="GtkBox" id="emoji_row_2">
<property name="valign">center</property>
<property name="halign">center</property>
<property name="spacing">30</property>
<property name="margin-bottom">24</property>
</object>
</child>
<child>
<object class="SpinnerButton" id="emoji_not_match_btn">
<property name="label" translatable="yes">Do Not Match</property>
<property name="halign">center</property>
<style>
<class name="destructive-action"/>
<class name="pill"/>
</style>
</object>
</child>
<child>
<object class="SpinnerButton" id="emoji_match_btn">
<property name="label" translatable="yes">Match</property>
<property name="halign">center</property>
<style>
<class name="suggested-action"/>
<class name="pill"/>
</style>
</object>
</child>
</object>
</property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">completed</property>
<property name="child">
<object class="AdwClamp">
<property name="maximum-size">400</property>
<property name="tightening-threshold">300</property>
<property name="child">
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="spacing">18</property>
<property name="valign">center</property>
<property name="halign">center</property>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">Request Complete</property>
<property name="wrap">True</property>
<property name="wrap-mode">word-char</property>
<property name="justify">center</property>
<style>
<class name="title-1"/>
</style>
</object>
</child>
<child>
<object class="GtkPicture">
<property name="file">resource:///org/gnome/FractalNext/icons/scalable/status/setup-complete.svg</property>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">The new session is now ready to send and receive secure messages.</property>
<property name="wrap">True</property>
<property name="wrap-mode">word-char</property>
<property name="justify">center</property>
</object>
</child>
<child>
<object class="GtkButton" id="done_btn">
<property name="label" translatable="yes">Done</property>
<property name="halign">center</property>
<style>
<class name="suggested-action"/>
<class name="pill"/>
</style>
</object>
</child>
</object>
</property>
</object>
</property>
</object>
</child>
</object>
</child>
</template>
</interface>

1
src/meson.build

@ -98,6 +98,7 @@ sources = files(
'session/verification/mod.rs',
'session/verification/emoji.rs',
'session/verification/identity_verification.rs',
'session/verification/incoming_verification.rs',
'session/verification/session_verification.rs',
'session/verification/verification_list.rs',
)

51
src/session/content/mod.rs

@ -18,7 +18,7 @@ use self::room_history::RoomHistory;
use self::state_row::StateRow;
use crate::session::sidebar::{Entry, EntryType};
use crate::session::verification::IdentityVerification;
use crate::session::verification::{IdentityVerification, IncomingVerification, VerificationMode};
use adw::subclass::prelude::*;
use gtk::{gio, glib, glib::clone, prelude::*, subclass::prelude::*, CompositeTemplate};
@ -40,7 +40,7 @@ mod imp {
pub session: RefCell<Option<WeakRef<Session>>>,
pub item: RefCell<Option<glib::Object>>,
pub error_list: RefCell<Option<gio::ListStore>>,
pub category_handler: RefCell<Option<SignalHandlerId>>,
pub signal_handler: RefCell<Option<SignalHandlerId>>,
#[template_child]
pub stack: TemplateChild<gtk::Stack>,
#[template_child]
@ -51,6 +51,10 @@ mod imp {
pub explore: TemplateChild<Explore>,
#[template_child]
pub empty_page: TemplateChild<gtk::Box>,
#[template_child]
pub verification_page: TemplateChild<gtk::Box>,
#[template_child]
pub incoming_verification: TemplateChild<IncomingVerification>,
}
#[glib::object_subclass]
@ -144,6 +148,17 @@ mod imp {
_ => unimplemented!(),
}
}
fn constructed(&self, obj: &Self::Type) {
self.parent_constructed(obj);
self.stack
.connect_visible_child_notify(clone!(@weak obj => move |stack| {
let priv_ = imp::Content::from_instance(&obj);
if stack.visible_child().as_ref() != Some(priv_.verification_page.upcast_ref::<gtk::Widget>()) {
priv_.incoming_verification.set_request(None);
}
}));
}
}
impl WidgetImpl for Content {}
@ -189,22 +204,32 @@ impl Content {
return;
}
if let Some(category_handler) = priv_.category_handler.take() {
if let Some(signal_handler) = priv_.signal_handler.take() {
if let Some(item) = self.item() {
item.disconnect(category_handler);
item.disconnect(signal_handler);
}
}
if let Some(ref room) = item {
if room.is::<Room>() {
let handler_id = room.connect_notify_local(
if let Some(ref item) = item {
if item.is::<Room>() {
let handler_id = item.connect_notify_local(
Some("category"),
clone!(@weak self as obj => move |_, _| {
obj.set_visible_child();
}),
);
priv_.category_handler.replace(Some(handler_id));
priv_.signal_handler.replace(Some(handler_id));
}
if item.is::<IdentityVerification>() {
let handler_id = item.connect_notify_local(Some("mode"), clone!(@weak self as obj => move |request, _| {
let request = request.downcast_ref::<IdentityVerification>().unwrap();
if request.mode() == VerificationMode::Cancelled || request.mode() == VerificationMode::Error || request.mode() == VerificationMode::Dismissed {
obj.set_item(None);
}
}));
priv_.signal_handler.replace(Some(handler_id));
}
}
@ -249,7 +274,15 @@ impl Content {
priv_.stack.set_visible_child(&*priv_.explore);
}
Some(o) if o.is::<IdentityVerification>() => {
todo!("Incoming verifications arn't implemented yet");
if let Some(item) = priv_
.item
.borrow()
.as_ref()
.and_then(|item| item.downcast_ref::<IdentityVerification>())
{
priv_.incoming_verification.set_request(Some(item.clone()));
priv_.stack.set_visible_child(&*priv_.verification_page);
}
}
_ => {}
}

425
src/session/verification/incoming_verification.rs

@ -0,0 +1,425 @@
use adw::subclass::prelude::*;
use gtk::{glib, glib::clone, prelude::*, subclass::prelude::*, CompositeTemplate};
use log::warn;
use crate::components::SpinnerButton;
use crate::contrib::screenshot;
use crate::contrib::QRCode;
use crate::contrib::QRCodeExt;
use crate::contrib::QrCodeScanner;
use crate::session::verification::{Emoji, IdentityVerification, VerificationMode};
use crate::spawn;
use gettextrs::gettext;
use matrix_sdk::encryption::verification::QrVerificationData;
mod imp {
use super::*;
use glib::subclass::InitializingObject;
use glib::SignalHandlerId;
use std::cell::RefCell;
#[derive(Debug, Default, CompositeTemplate)]
#[template(resource = "/org/gnome/FractalNext/incoming-verification.ui")]
pub struct IncomingVerification {
pub request: RefCell<Option<IdentityVerification>>,
#[template_child]
pub qrcode: TemplateChild<QRCode>,
#[template_child]
pub emoji_row_1: TemplateChild<gtk::Box>,
#[template_child]
pub emoji_row_2: TemplateChild<gtk::Box>,
#[template_child]
pub emoji_match_btn: TemplateChild<SpinnerButton>,
#[template_child]
pub emoji_not_match_btn: TemplateChild<SpinnerButton>,
#[template_child]
pub start_emoji_btn: TemplateChild<SpinnerButton>,
#[template_child]
pub start_emoji_btn2: TemplateChild<SpinnerButton>,
#[template_child]
pub start_emoji_btn3: TemplateChild<SpinnerButton>,
#[template_child]
pub scan_qr_code_btn: TemplateChild<SpinnerButton>,
#[template_child]
pub accept_btn: TemplateChild<SpinnerButton>,
#[template_child]
pub dismiss_btn: TemplateChild<gtk::Button>,
#[template_child]
pub take_screenshot_btn2: TemplateChild<SpinnerButton>,
#[template_child]
pub take_screenshot_btn3: TemplateChild<SpinnerButton>,
#[template_child]
pub main_stack: TemplateChild<gtk::Stack>,
#[template_child]
pub qr_code_scanner: TemplateChild<QrCodeScanner>,
#[template_child]
pub done_btn: TemplateChild<gtk::Button>,
pub mode_handler: RefCell<Option<SignalHandlerId>>,
}
#[glib::object_subclass]
impl ObjectSubclass for IncomingVerification {
const NAME: &'static str = "IncomingVerification";
type Type = super::IncomingVerification;
type ParentType = adw::Bin;
fn class_init(klass: &mut Self::Class) {
SpinnerButton::static_type();
QRCode::static_type();
Emoji::static_type();
QrCodeScanner::static_type();
klass.install_action("verification.dismiss", None, move |obj, _, _| {
obj.dismiss();
});
Self::bind_template(klass);
}
fn instance_init(obj: &InitializingObject<Self>) {
obj.init_template();
}
}
impl ObjectImpl for IncomingVerification {
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 Object holding the data for the verification",
IdentityVerification::static_type(),
glib::ParamFlags::READWRITE | glib::ParamFlags::EXPLICIT_NOTIFY,
)]
});
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!(),
}
}
fn constructed(&self, obj: &Self::Type) {
self.parent_constructed(obj);
self.accept_btn
.connect_clicked(clone!(@weak obj => move |button| {
let priv_ = imp::IncomingVerification::from_instance(&obj);
button.set_loading(true);
priv_.dismiss_btn.set_sensitive(false);
obj.accept();
}));
self.emoji_match_btn
.connect_clicked(clone!(@weak obj => move |button| {
let priv_ = imp::IncomingVerification::from_instance(&obj);
button.set_loading(true);
priv_.emoji_not_match_btn.set_sensitive(false);
if let Some(request) = obj.request() {
request.emoji_match();
}
}));
self.emoji_not_match_btn
.connect_clicked(clone!(@weak obj => move |button| {
let priv_ = imp::IncomingVerification::from_instance(&obj);
button.set_loading(true);
priv_.emoji_match_btn.set_sensitive(false);
if let Some(request) = obj.request() {
request.emoji_not_match();
}
}));
self.start_emoji_btn
.connect_clicked(clone!(@weak obj => move |button| {
let priv_ = imp::IncomingVerification::from_instance(&obj);
button.set_loading(true);
priv_.scan_qr_code_btn.set_sensitive(false);
if let Some(request) = obj.request() {
request.start_sas();
}
}));
self.start_emoji_btn2
.connect_clicked(clone!(@weak obj => move |button| {
let priv_ = imp::IncomingVerification::from_instance(&obj);
button.set_loading(true);
priv_.take_screenshot_btn2.set_sensitive(false);
if let Some(request) = obj.request() {
request.start_sas();
}
}));
self.start_emoji_btn3
.connect_clicked(clone!(@weak obj => move |button| {
let priv_ = imp::IncomingVerification::from_instance(&obj);
button.set_loading(true);
priv_.take_screenshot_btn3.set_sensitive(false);
if let Some(request) = obj.request() {
request.start_sas();
}
}));
self.scan_qr_code_btn
.connect_clicked(clone!(@weak obj => move |button| {
let priv_ = imp::IncomingVerification::from_instance(&obj);
button.set_loading(true);
priv_.start_emoji_btn.set_sensitive(false);
if priv_.qr_code_scanner.has_camera() {
obj.start_scanning();
} else {
obj.take_screenshot();
}
}));
self.take_screenshot_btn2
.connect_clicked(clone!(@weak obj => move |button| {
let priv_ = imp::IncomingVerification::from_instance(&obj);
button.set_loading(true);
priv_.start_emoji_btn2.set_sensitive(false);
obj.take_screenshot();
}));
self.take_screenshot_btn3
.connect_clicked(clone!(@weak obj => move |button| {
let priv_ = imp::IncomingVerification::from_instance(&obj);
button.set_loading(true);
priv_.start_emoji_btn3.set_sensitive(false);
obj.take_screenshot();
}));
self.done_btn.connect_clicked(clone!(@weak obj => move |_| {
obj.dismiss();
}));
self.qr_code_scanner
.connect_code_detected(clone!(@weak obj => move |_, data| {
obj.finish_scanning(data);
}));
self.qr_code_scanner.connect_notify_local(
Some("has-camera"),
clone!(@weak obj => move |_, _| {
obj.update_camera_state();
}),
);
obj.update_camera_state();
}
fn dispose(&self, obj: &Self::Type) {
if let Some(request) = obj.request() {
if let Some(handler) = self.mode_handler.take() {
request.disconnect(handler);
}
}
}
}
impl WidgetImpl for IncomingVerification {
fn map(&self, widget: &Self::Type) {
self.parent_map(widget);
widget.update_view();
}
}
impl BinImpl for IncomingVerification {}
}
glib::wrapper! {
pub struct IncomingVerification(ObjectSubclass<imp::IncomingVerification>)
@extends gtk::Widget, adw::Bin, @implements gtk::Accessible;
}
impl IncomingVerification {
pub fn new(request: &IdentityVerification) -> Self {
glib::Object::new(&[("request", request)]).expect("Failed to create IncomingVerification")
}
pub fn request(&self) -> Option<IdentityVerification> {
let priv_ = imp::IncomingVerification::from_instance(self);
priv_.request.borrow().clone()
}
pub fn set_request(&self, request: Option<IdentityVerification>) {
let priv_ = imp::IncomingVerification::from_instance(self);
let previous_request = self.request();
if previous_request == request {
return;
}
self.reset();
if let Some(previous_request) = previous_request {
if let Some(handler) = priv_.mode_handler.take() {
previous_request.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();
}),
);
self.update_view();
priv_.mode_handler.replace(Some(handler));
}
priv_.request.replace(request);
self.notify("request");
}
fn reset(&self) {
let priv_ = imp::IncomingVerification::from_instance(self);
priv_.accept_btn.set_loading(false);
priv_.accept_btn.set_sensitive(true);
priv_.dismiss_btn.set_sensitive(true);
priv_.scan_qr_code_btn.set_loading(false);
priv_.scan_qr_code_btn.set_sensitive(true);
priv_.emoji_not_match_btn.set_loading(false);
priv_.emoji_not_match_btn.set_sensitive(true);
priv_.emoji_match_btn.set_loading(false);
priv_.emoji_match_btn.set_sensitive(true);
priv_.start_emoji_btn.set_loading(false);
priv_.start_emoji_btn.set_sensitive(true);
priv_.start_emoji_btn2.set_loading(false);
priv_.start_emoji_btn2.set_sensitive(true);
priv_.start_emoji_btn3.set_loading(false);
priv_.start_emoji_btn3.set_sensitive(true);
priv_.take_screenshot_btn2.set_loading(false);
priv_.take_screenshot_btn2.set_sensitive(true);
priv_.take_screenshot_btn3.set_loading(false);
priv_.take_screenshot_btn3.set_sensitive(true);
self.clean_emoji();
}
fn clean_emoji(&self) {
let priv_ = imp::IncomingVerification::from_instance(self);
while let Some(child) = priv_.emoji_row_1.first_child() {
priv_.emoji_row_1.remove(&child);
}
while let Some(child) = priv_.emoji_row_2.first_child() {
priv_.emoji_row_2.remove(&child);
}
}
pub fn accept(&self) {
if let Some(request) = self.request() {
request.accept_incoming();
}
}
pub fn dismiss(&self) {
if let Some(request) = self.request() {
request.dismiss();
}
}
fn update_view(&self) {
let priv_ = imp::IncomingVerification::from_instance(self);
if let Some(request) = self.request() {
match request.mode() {
VerificationMode::IdentityNotFound => {
// TODO: what should we do if we don't find the identity
}
VerificationMode::Requested => {
priv_.main_stack.set_visible_child_name("accept-request");
}
VerificationMode::QrV1Show => {
if let Some(qrcode) = request.qr_code() {
priv_.qrcode.set_qrcode(qrcode);
priv_.main_stack.set_visible_child_name("qrcode");
} else {
warn!("Failed to get qrcode for QrVerification");
request.start_sas();
}
}
VerificationMode::QrV1Scan => {
self.start_scanning();
}
VerificationMode::SasV1 => {
self.clean_emoji();
// TODO: implement sas fallback when emojis arn't supported
if let Some(emoji) = request.emoji() {
for (index, emoji) in emoji.iter().enumerate() {
if index < 4 {
priv_.emoji_row_1.append(&Emoji::new(emoji));
} else {
priv_.emoji_row_2.append(&Emoji::new(emoji));
}
}
priv_.main_stack.set_visible_child_name("emoji");
}
}
VerificationMode::Completed => {
priv_.main_stack.set_visible_child_name("completed");
}
_ => {}
}
}
}
fn start_scanning(&self) {
spawn!(clone!(@weak self as obj => async move {
let priv_ = imp::IncomingVerification::from_instance(&obj);
if priv_.qr_code_scanner.start().await {
priv_.main_stack.set_visible_child_name("scan-qr-code");
} else {
priv_.main_stack.set_visible_child_name("no-camera");
}
}));
}
fn take_screenshot(&self) {
spawn!(clone!(@weak self as obj => async move {
let root = obj.root().unwrap();
if let Some(code) = screenshot::capture(&root).await {
obj.finish_scanning(code);
} else {
obj.reset();
}
}));
}
fn finish_scanning(&self, data: QrVerificationData) {
let priv_ = imp::IncomingVerification::from_instance(self);
priv_.qr_code_scanner.stop();
if let Some(request) = self.request() {
request.scanned_qr_code(data);
}
priv_.main_stack.set_visible_child_name("qr-code-scanned");
}
fn update_camera_state(&self) {
let priv_ = imp::IncomingVerification::from_instance(self);
if priv_.qr_code_scanner.has_camera() {
priv_
.scan_qr_code_btn
.set_label(&gettext("Scan QR code with this session"))
} else {
priv_
.scan_qr_code_btn
.set_label(&gettext("Take a Screenshot of a Qr Code"))
}
}
}

2
src/session/verification/mod.rs

@ -1,9 +1,11 @@
mod emoji;
mod identity_verification;
mod incoming_verification;
mod session_verification;
mod verification_list;
pub use self::emoji::Emoji;
pub use self::identity_verification::{IdentityVerification, Mode as VerificationMode};
pub use self::incoming_verification::IncomingVerification;
pub use self::session_verification::SessionVerification;
pub use self::verification_list::VerificationList;

39
src/session/verification/verification_list.rs

@ -1,7 +1,10 @@
use gtk::{gio, glib, prelude::*, subclass::prelude::*};
use gtk::{gio, glib, glib::clone, prelude::*, subclass::prelude::*};
use matrix_sdk::ruma::{api::client::r0::sync::sync_events::ToDevice, events::AnyToDeviceEvent};
use crate::session::{verification::IdentityVerification, Session};
use crate::session::{
verification::{IdentityVerification, VerificationMode},
Session,
};
mod imp {
use glib::object::WeakRef;
@ -99,8 +102,10 @@ impl VerificationList {
let priv_ = imp::VerificationList::from_instance(self);
for event in &to_device.events {
if let Ok(AnyToDeviceEvent::KeyVerificationRequest(_event)) = event.deserialize() {
//TODO: implement handling of incomming requests
if let Ok(AnyToDeviceEvent::KeyVerificationRequest(event)) = event.deserialize() {
let request = IdentityVerification::new(self.session().user().unwrap());
request.set_flow_id(Some(event.content.transaction_id.to_owned()));
self.add(request);
}
}
@ -115,9 +120,35 @@ impl VerificationList {
let length = {
let mut list = priv_.list.borrow_mut();
let length = list.len();
request.connect_notify_local(Some("mode"), clone!(@weak self as obj => move |request, _| {
if request.mode() == VerificationMode::Error || request.mode() == VerificationMode::Cancelled || request.mode() == VerificationMode::Dismissed || request.mode() == VerificationMode::Completed {
obj.remove(request);
}
}));
list.push(request);
length as u32
};
self.items_changed(length, 0, 1)
}
pub fn remove(&self, request: &IdentityVerification) {
let priv_ = imp::VerificationList::from_instance(self);
let position = {
let mut list = priv_.list.borrow_mut();
let mut position = None;
for (index, item) in list.iter().enumerate() {
if item == request {
position = Some(index);
break;
}
}
if let Some(position) = position {
list.remove(position);
}
position
};
if let Some(position) = position {
self.items_changed(position as u32, 1, 0);
}
}
}

Loading…
Cancel
Save