232 lines
7.0 KiB
Rust
232 lines
7.0 KiB
Rust
use adw::{prelude::*, subclass::prelude::*};
|
|
use gettextrs::gettext;
|
|
use gtk::{glib, glib::clone, CompositeTemplate};
|
|
|
|
use crate::{
|
|
components::{Avatar, SpinnerButton},
|
|
prelude::*,
|
|
session::model::User,
|
|
spawn, toast,
|
|
utils::BoundObject,
|
|
Window,
|
|
};
|
|
|
|
mod imp {
|
|
use std::cell::RefCell;
|
|
|
|
use glib::subclass::InitializingObject;
|
|
|
|
use super::*;
|
|
|
|
#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
|
|
#[template(resource = "/org/gnome/Fractal/ui/session/view/user_page.ui")]
|
|
#[properties(wrapper_type = super::UserPage)]
|
|
pub struct UserPage {
|
|
#[template_child]
|
|
pub avatar: TemplateChild<Avatar>,
|
|
#[template_child]
|
|
pub direct_chat_button: TemplateChild<SpinnerButton>,
|
|
#[template_child]
|
|
pub verified_row: TemplateChild<adw::ActionRow>,
|
|
#[template_child]
|
|
pub verified_stack: TemplateChild<gtk::Stack>,
|
|
#[template_child]
|
|
pub verify_button: TemplateChild<SpinnerButton>,
|
|
/// The current user.
|
|
#[property(get, set = Self::set_user, construct_only)]
|
|
pub user: BoundObject<User>,
|
|
pub bindings: RefCell<Vec<glib::Binding>>,
|
|
}
|
|
|
|
#[glib::object_subclass]
|
|
impl ObjectSubclass for UserPage {
|
|
const NAME: &'static str = "UserPage";
|
|
type Type = super::UserPage;
|
|
type ParentType = adw::NavigationPage;
|
|
|
|
fn class_init(klass: &mut Self::Class) {
|
|
Self::bind_template(klass);
|
|
|
|
klass.install_action_async(
|
|
"user-page.open-direct-chat",
|
|
None,
|
|
|widget, _, _| async move {
|
|
widget.open_direct_chat().await;
|
|
},
|
|
);
|
|
|
|
klass.install_action_async("user-page.verify-user", None, |widget, _, _| async move {
|
|
widget.verify_user().await;
|
|
});
|
|
}
|
|
|
|
fn instance_init(obj: &InitializingObject<Self>) {
|
|
obj.init_template();
|
|
}
|
|
}
|
|
|
|
#[glib::derived_properties]
|
|
impl ObjectImpl for UserPage {
|
|
fn dispose(&self) {
|
|
for binding in self.bindings.take() {
|
|
binding.unbind();
|
|
}
|
|
}
|
|
}
|
|
|
|
impl WidgetImpl for UserPage {}
|
|
impl NavigationPageImpl for UserPage {}
|
|
|
|
impl UserPage {
|
|
/// Set the current user.
|
|
fn set_user(&self, user: User) {
|
|
let obj = self.obj();
|
|
|
|
let title_binding = user
|
|
.bind_property("display-name", &*obj, "title")
|
|
.sync_create()
|
|
.build();
|
|
let avatar_binding = user
|
|
.bind_property("avatar-data", &*self.avatar, "data")
|
|
.sync_create()
|
|
.build();
|
|
self.bindings.replace(vec![title_binding, avatar_binding]);
|
|
|
|
let is_verified_handler = user.connect_verified_notify(clone!(@weak obj => move |_| {
|
|
obj.update_verified();
|
|
}));
|
|
|
|
// We don't need to listen to changes of the property, it never changes after
|
|
// construction.
|
|
self.direct_chat_button.set_visible(!user.is_own_user());
|
|
|
|
self.user.set(user, vec![is_verified_handler]);
|
|
|
|
spawn!(clone!(@weak obj => async move {
|
|
obj.load_direct_chat().await;
|
|
}));
|
|
obj.update_verified();
|
|
}
|
|
}
|
|
}
|
|
|
|
glib::wrapper! {
|
|
/// Page to view details about a user.
|
|
pub struct UserPage(ObjectSubclass<imp::UserPage>)
|
|
@extends gtk::Widget, adw::NavigationPage, @implements gtk::Accessible;
|
|
}
|
|
|
|
impl UserPage {
|
|
/// Construct a new `UserPage` for the given user.
|
|
pub fn new(user: &impl IsA<User>) -> Self {
|
|
glib::Object::builder().property("user", user).build()
|
|
}
|
|
|
|
/// Load whether the current user has a direct chat or not.
|
|
async fn load_direct_chat(&self) {
|
|
self.set_direct_chat_loading(true);
|
|
|
|
let Some(user) = self.user() else {
|
|
return;
|
|
};
|
|
|
|
let direct_chat = user.direct_chat().await;
|
|
|
|
let label = if direct_chat.is_some() {
|
|
gettext("Open Direct Chat")
|
|
} else {
|
|
gettext("Create Direct Chat")
|
|
};
|
|
self.imp().direct_chat_button.set_label(label);
|
|
|
|
self.set_direct_chat_loading(false);
|
|
}
|
|
|
|
/// Set whether the direct chat button is loading.
|
|
fn set_direct_chat_loading(&self, loading: bool) {
|
|
self.action_set_enabled("user-page.open-direct-chat", !loading);
|
|
self.imp().direct_chat_button.set_loading(loading);
|
|
}
|
|
|
|
/// Open a direct chat with the current user.
|
|
///
|
|
/// If one doesn't exist already, it is created.
|
|
async fn open_direct_chat(&self) {
|
|
let Some(user) = self.user() else {
|
|
return;
|
|
};
|
|
|
|
self.set_direct_chat_loading(true);
|
|
|
|
let Ok(room) = user.get_or_create_direct_chat().await else {
|
|
toast!(self, &gettext("Failed to create a new Direct Chat"));
|
|
self.set_direct_chat_loading(false);
|
|
|
|
return;
|
|
};
|
|
|
|
let Some(parent_window) = self.root().and_downcast::<gtk::Window>() else {
|
|
return;
|
|
};
|
|
|
|
if let Some(main_window) = parent_window.transient_for().and_downcast::<Window>() {
|
|
main_window.show_room(user.session().session_id(), room.room_id());
|
|
}
|
|
|
|
parent_window.close();
|
|
}
|
|
|
|
/// Update the verified row.
|
|
fn update_verified(&self) {
|
|
let Some(user) = self.user() else {
|
|
return;
|
|
};
|
|
let imp = self.imp();
|
|
|
|
if user.verified() {
|
|
imp.verified_row.set_title(&gettext("Identity verified"));
|
|
imp.verified_stack.set_visible_child_name("icon");
|
|
self.action_set_enabled("user-page.verify-user", false);
|
|
} else {
|
|
self.action_set_enabled("user-page.verify-user", true);
|
|
imp.verified_stack.set_visible_child_name("button");
|
|
imp.verified_row
|
|
.set_title(&gettext("Identity not verified"));
|
|
}
|
|
}
|
|
|
|
/// Launch the verification for the current user.
|
|
async fn verify_user(&self) {
|
|
let Some(user) = self.user() else {
|
|
return;
|
|
};
|
|
let imp = self.imp();
|
|
|
|
self.action_set_enabled("user-page.verify-user", false);
|
|
imp.verify_button.set_loading(true);
|
|
let verification = user.verify_identity().await;
|
|
|
|
let Some(flow_id) = verification.flow_id() else {
|
|
toast!(self, gettext("Failed to start user verification"));
|
|
self.action_set_enabled("user-page.verify-user", true);
|
|
imp.verify_button.set_loading(false);
|
|
|
|
return;
|
|
};
|
|
|
|
let Some(parent_window) = self.root().and_downcast::<gtk::Window>() else {
|
|
return;
|
|
};
|
|
|
|
if let Some(main_window) = parent_window.transient_for().and_downcast::<Window>() {
|
|
main_window.show_verification(
|
|
user.session().session_id(),
|
|
&UserExt::user_id(&user),
|
|
flow_id,
|
|
);
|
|
}
|
|
|
|
parent_window.close();
|
|
}
|
|
}
|