account-settings: Port to glib::Properties macro
This commit is contained in:
parent
118f4ca1b0
commit
ca4ec3100d
|
@ -3,7 +3,7 @@ use matrix_sdk::{
|
|||
encryption::identities::Device as CryptoDevice,
|
||||
ruma::{
|
||||
api::client::device::{delete_device, Device as MatrixDevice},
|
||||
assign, DeviceId,
|
||||
assign,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -13,16 +13,35 @@ use crate::{
|
|||
};
|
||||
|
||||
mod imp {
|
||||
use glib::object::WeakRef;
|
||||
use once_cell::{sync::Lazy, unsync::OnceCell};
|
||||
use std::{cell::OnceCell, marker::PhantomData};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, Default, glib::Properties)]
|
||||
#[properties(wrapper_type = super::Device)]
|
||||
pub struct Device {
|
||||
/// The device data.
|
||||
pub device: OnceCell<MatrixDevice>,
|
||||
/// The encryption API of the device.
|
||||
pub crypto_device: OnceCell<CryptoDevice>,
|
||||
pub session: WeakRef<Session>,
|
||||
/// The current session.
|
||||
#[property(get, construct_only)]
|
||||
pub session: glib::WeakRef<Session>,
|
||||
/// The ID of the device.
|
||||
#[property(get = Self::device_id)]
|
||||
device_id: PhantomData<String>,
|
||||
/// The display name of the device.
|
||||
#[property(get = Self::display_name)]
|
||||
display_name: PhantomData<String>,
|
||||
/// The last IP address the device used.
|
||||
#[property(get = Self::last_seen_ip)]
|
||||
last_seen_ip: PhantomData<Option<String>>,
|
||||
/// The last time the device was used.
|
||||
#[property(get = Self::last_seen_ts)]
|
||||
last_seen_ts: PhantomData<Option<glib::DateTime>>,
|
||||
/// Whether this device is verified.
|
||||
#[property(get = Self::verified)]
|
||||
verified: PhantomData<bool>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
|
@ -31,53 +50,50 @@ mod imp {
|
|||
type Type = super::Device;
|
||||
}
|
||||
|
||||
impl ObjectImpl for Device {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![
|
||||
glib::ParamSpecObject::builder::<Session>("session")
|
||||
.construct_only()
|
||||
.build(),
|
||||
glib::ParamSpecString::builder("device-id")
|
||||
.read_only()
|
||||
.build(),
|
||||
glib::ParamSpecString::builder("display-name")
|
||||
.read_only()
|
||||
.build(),
|
||||
glib::ParamSpecString::builder("last-seen-ip")
|
||||
.read_only()
|
||||
.build(),
|
||||
glib::ParamSpecBoxed::builder::<glib::DateTime>("last-seen-ts")
|
||||
.read_only()
|
||||
.build(),
|
||||
glib::ParamSpecBoolean::builder("verified")
|
||||
.read_only()
|
||||
.build(),
|
||||
]
|
||||
});
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for Device {}
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
impl Device {
|
||||
/// The device data.
|
||||
fn device(&self) -> &MatrixDevice {
|
||||
self.device.get().unwrap()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
match pspec.name() {
|
||||
"session" => self.session.set(value.get().ok().as_ref()),
|
||||
_ => unimplemented!(),
|
||||
/// The ID of this device.
|
||||
fn device_id(&self) -> String {
|
||||
self.device().device_id.to_string()
|
||||
}
|
||||
|
||||
/// The display name of the device.
|
||||
fn display_name(&self) -> String {
|
||||
if let Some(display_name) = self.device().display_name.clone() {
|
||||
display_name
|
||||
} else {
|
||||
self.device_id()
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
let obj = self.obj();
|
||||
/// The last IP address the device used.
|
||||
fn last_seen_ip(&self) -> Option<String> {
|
||||
// TODO: Would be nice to also show the location
|
||||
// See: https://gitlab.gnome.org/GNOME/fractal/-/issues/700
|
||||
self.device().last_seen_ip.clone()
|
||||
}
|
||||
|
||||
match pspec.name() {
|
||||
"session" => obj.session().to_value(),
|
||||
"display-name" => obj.display_name().to_value(),
|
||||
"device-id" => obj.device_id().as_str().to_value(),
|
||||
"last-seen-ip" => obj.last_seen_ip().to_value(),
|
||||
"last-seen-ts" => obj.last_seen_ts().to_value(),
|
||||
"verified" => obj.is_verified().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
/// The last time the device was used.
|
||||
fn last_seen_ts(&self) -> Option<glib::DateTime> {
|
||||
self.device().last_seen_ts.map(|last_seen_ts| {
|
||||
glib::DateTime::from_unix_utc(last_seen_ts.as_secs().into())
|
||||
.and_then(|t| t.to_local())
|
||||
.unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
/// Whether this device is verified.
|
||||
fn verified(&self) -> bool {
|
||||
self.crypto_device
|
||||
.get()
|
||||
.is_some_and(|device| device.is_verified())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -100,11 +116,6 @@ impl Device {
|
|||
obj
|
||||
}
|
||||
|
||||
/// The current session.
|
||||
pub fn session(&self) -> Session {
|
||||
self.imp().session.upgrade().unwrap()
|
||||
}
|
||||
|
||||
/// Set the Matrix device of this `Device`.
|
||||
fn set_matrix_device(&self, device: MatrixDevice, crypto_device: Option<CryptoDevice>) {
|
||||
let imp = self.imp();
|
||||
|
@ -114,48 +125,15 @@ impl Device {
|
|||
}
|
||||
}
|
||||
|
||||
/// The ID of this device.
|
||||
pub fn device_id(&self) -> &DeviceId {
|
||||
&self.imp().device.get().unwrap().device_id
|
||||
}
|
||||
|
||||
/// The display name of the device.
|
||||
pub fn display_name(&self) -> &str {
|
||||
if let Some(ref display_name) = self.imp().device.get().unwrap().display_name {
|
||||
display_name
|
||||
} else {
|
||||
self.device_id().as_str()
|
||||
}
|
||||
}
|
||||
|
||||
/// The last IP address the device used.
|
||||
pub fn last_seen_ip(&self) -> Option<&str> {
|
||||
// TODO: Would be nice to also show the location
|
||||
// See: https://gitlab.gnome.org/GNOME/fractal/-/issues/700
|
||||
self.imp().device.get().unwrap().last_seen_ip.as_deref()
|
||||
}
|
||||
|
||||
/// The last time the device was used.
|
||||
pub fn last_seen_ts(&self) -> Option<glib::DateTime> {
|
||||
self.imp()
|
||||
.device
|
||||
.get()
|
||||
.unwrap()
|
||||
.last_seen_ts
|
||||
.map(|last_seen_ts| {
|
||||
glib::DateTime::from_unix_utc(last_seen_ts.as_secs().into())
|
||||
.and_then(|t| t.to_local())
|
||||
.unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
/// Deletes the `Device`.
|
||||
pub async fn delete(
|
||||
&self,
|
||||
transient_for: Option<&impl IsA<gtk::Window>>,
|
||||
) -> Result<(), AuthError> {
|
||||
let session = self.session();
|
||||
let device_id = self.device_id().to_owned();
|
||||
let Some(session) = self.session() else {
|
||||
return Err(AuthError::NoSession);
|
||||
};
|
||||
let device_id = self.imp().device.get().unwrap().device_id.clone();
|
||||
|
||||
let dialog = AuthDialog::new(transient_for, &session);
|
||||
|
||||
|
@ -170,12 +148,4 @@ impl Device {
|
|||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Whether this device is verified.
|
||||
pub fn is_verified(&self) -> bool {
|
||||
self.imp()
|
||||
.crypto_device
|
||||
.get()
|
||||
.map_or(false, |device| device.is_verified())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,86 +3,61 @@ use gtk::{glib, prelude::*, subclass::prelude::*};
|
|||
use super::Device;
|
||||
|
||||
/// This enum contains all possible types the device list can hold.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ItemType {
|
||||
#[derive(Debug, Clone, glib::Boxed)]
|
||||
#[boxed_type(name = "DeviceListItemType")]
|
||||
pub enum DeviceListItemType {
|
||||
Device(Device),
|
||||
Error(String),
|
||||
LoadingSpinner,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, glib::Boxed)]
|
||||
#[boxed_type(name = "BoxedDeviceItemType")]
|
||||
pub struct BoxedItemType(ItemType);
|
||||
|
||||
impl From<ItemType> for BoxedItemType {
|
||||
fn from(type_: ItemType) -> Self {
|
||||
BoxedItemType(type_)
|
||||
}
|
||||
}
|
||||
|
||||
mod imp {
|
||||
use once_cell::{sync::Lazy, unsync::OnceCell};
|
||||
use std::cell::OnceCell;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Item {
|
||||
pub type_: OnceCell<ItemType>,
|
||||
#[derive(Debug, Default, glib::Properties)]
|
||||
#[properties(wrapper_type = super::DeviceListItem)]
|
||||
pub struct DeviceListItem {
|
||||
/// The type of this item.
|
||||
#[property(get, construct_only)]
|
||||
pub item_type: OnceCell<DeviceListItemType>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for Item {
|
||||
const NAME: &'static str = "DeviceItem";
|
||||
type Type = super::Item;
|
||||
impl ObjectSubclass for DeviceListItem {
|
||||
const NAME: &'static str = "DeviceListItem";
|
||||
type Type = super::DeviceListItem;
|
||||
}
|
||||
|
||||
impl ObjectImpl for Item {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![glib::ParamSpecBoxed::builder::<BoxedItemType>("type")
|
||||
.write_only()
|
||||
.construct_only()
|
||||
.build()]
|
||||
});
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
match pspec.name() {
|
||||
"type" => {
|
||||
let type_ = value.get::<BoxedItemType>().unwrap();
|
||||
self.type_.set(type_.0).unwrap();
|
||||
}
|
||||
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for DeviceListItem {}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct Item(ObjectSubclass<imp::Item>);
|
||||
/// An item in the device list.
|
||||
pub struct DeviceListItem(ObjectSubclass<imp::DeviceListItem>);
|
||||
}
|
||||
|
||||
impl Item {
|
||||
impl DeviceListItem {
|
||||
pub fn for_device(device: Device) -> Self {
|
||||
let type_ = BoxedItemType(ItemType::Device(device));
|
||||
glib::Object::builder().property("type", &type_).build()
|
||||
let item_type = DeviceListItemType::Device(device);
|
||||
glib::Object::builder()
|
||||
.property("item-type", &item_type)
|
||||
.build()
|
||||
}
|
||||
|
||||
pub fn for_error(error: String) -> Self {
|
||||
let type_ = BoxedItemType(ItemType::Error(error));
|
||||
glib::Object::builder().property("type", &type_).build()
|
||||
let item_type = DeviceListItemType::Error(error);
|
||||
glib::Object::builder()
|
||||
.property("item-type", &item_type)
|
||||
.build()
|
||||
}
|
||||
|
||||
pub fn for_loading_spinner() -> Self {
|
||||
let type_ = BoxedItemType(ItemType::LoadingSpinner);
|
||||
glib::Object::builder().property("type", &type_).build()
|
||||
}
|
||||
|
||||
/// The type of this item.
|
||||
pub fn type_(&self) -> &ItemType {
|
||||
self.imp().type_.get().unwrap()
|
||||
let item_type = DeviceListItemType::LoadingSpinner;
|
||||
glib::Object::builder()
|
||||
.property("item-type", &item_type)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,22 +6,31 @@ use matrix_sdk::{
|
|||
};
|
||||
use tracing::error;
|
||||
|
||||
use super::{Device, DeviceItem};
|
||||
use super::{Device, DeviceListItem};
|
||||
use crate::{session::model::Session, spawn, spawn_tokio};
|
||||
|
||||
mod imp {
|
||||
use std::cell::{Cell, RefCell};
|
||||
|
||||
use glib::object::WeakRef;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
marker::PhantomData,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, Default, glib::Properties)]
|
||||
#[properties(wrapper_type = super::DeviceList)]
|
||||
pub struct DeviceList {
|
||||
pub list: RefCell<Vec<DeviceItem>>,
|
||||
pub session: WeakRef<Session>,
|
||||
pub current_device: RefCell<Option<DeviceItem>>,
|
||||
/// The list of device list items.
|
||||
pub list: RefCell<Vec<DeviceListItem>>,
|
||||
/// The current session.
|
||||
#[property(get, construct_only)]
|
||||
pub session: glib::WeakRef<Session>,
|
||||
/// The device of this session.
|
||||
pub current_device_inner: RefCell<Option<DeviceListItem>>,
|
||||
/// The device of this session, or a replacement list item if it is not
|
||||
/// found.
|
||||
#[property(get = Self::current_device)]
|
||||
current_device: PhantomData<DeviceListItem>,
|
||||
pub loading: Cell<bool>,
|
||||
}
|
||||
|
||||
|
@ -32,39 +41,8 @@ mod imp {
|
|||
type Interfaces = (gio::ListModel,);
|
||||
}
|
||||
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for DeviceList {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![
|
||||
glib::ParamSpecObject::builder::<Session>("session")
|
||||
.construct_only()
|
||||
.build(),
|
||||
glib::ParamSpecObject::builder::<DeviceItem>("current-device")
|
||||
.read_only()
|
||||
.build(),
|
||||
]
|
||||
});
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
match pspec.name() {
|
||||
"session" => self.session.set(value.get().ok().as_ref()),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
let obj = self.obj();
|
||||
|
||||
match pspec.name() {
|
||||
"session" => obj.session().to_value(),
|
||||
"current-device" => obj.current_device().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
self.obj().load_devices();
|
||||
|
@ -73,11 +51,13 @@ mod imp {
|
|||
|
||||
impl ListModelImpl for DeviceList {
|
||||
fn item_type(&self) -> glib::Type {
|
||||
DeviceItem::static_type()
|
||||
DeviceListItem::static_type()
|
||||
}
|
||||
|
||||
fn n_items(&self) -> u32 {
|
||||
self.list.borrow().len() as u32
|
||||
}
|
||||
|
||||
fn item(&self, position: u32) -> Option<glib::Object> {
|
||||
self.list
|
||||
.borrow()
|
||||
|
@ -86,6 +66,22 @@ mod imp {
|
|||
.cloned()
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceList {
|
||||
/// The device of this session.
|
||||
fn current_device(&self) -> DeviceListItem {
|
||||
self.current_device_inner
|
||||
.borrow()
|
||||
.clone()
|
||||
.unwrap_or_else(|| {
|
||||
if self.loading.get() {
|
||||
DeviceListItem::for_loading_spinner()
|
||||
} else {
|
||||
DeviceListItem::for_error(gettext("Failed to load connected device."))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
|
@ -99,11 +95,6 @@ impl DeviceList {
|
|||
glib::Object::builder().property("session", session).build()
|
||||
}
|
||||
|
||||
/// The current session.
|
||||
pub fn session(&self) -> Session {
|
||||
self.imp().session.upgrade().unwrap()
|
||||
}
|
||||
|
||||
fn set_loading(&self, loading: bool) {
|
||||
let imp = self.imp();
|
||||
|
||||
|
@ -111,39 +102,20 @@ impl DeviceList {
|
|||
return;
|
||||
}
|
||||
if loading {
|
||||
self.update_list(vec![DeviceItem::for_loading_spinner()]);
|
||||
self.update_list(vec![DeviceListItem::for_loading_spinner()]);
|
||||
}
|
||||
imp.loading.set(loading);
|
||||
self.notify("current-device");
|
||||
}
|
||||
|
||||
fn loading(&self) -> bool {
|
||||
self.imp().loading.get()
|
||||
}
|
||||
|
||||
/// The device of this session.
|
||||
pub fn current_device(&self) -> DeviceItem {
|
||||
self.imp()
|
||||
.current_device
|
||||
.borrow()
|
||||
.clone()
|
||||
.unwrap_or_else(|| {
|
||||
if self.loading() {
|
||||
DeviceItem::for_loading_spinner()
|
||||
} else {
|
||||
DeviceItem::for_error(gettext("Failed to load connected device."))
|
||||
}
|
||||
})
|
||||
self.notify_current_device();
|
||||
}
|
||||
|
||||
/// Set the device of this session.
|
||||
fn set_current_device(&self, device: Option<DeviceItem>) {
|
||||
self.imp().current_device.replace(device);
|
||||
fn set_current_device(&self, device: Option<DeviceListItem>) {
|
||||
self.imp().current_device_inner.replace(device);
|
||||
|
||||
self.notify("current-device");
|
||||
self.notify_current_device();
|
||||
}
|
||||
|
||||
fn update_list(&self, devices: Vec<DeviceItem>) {
|
||||
fn update_list(&self, devices: Vec<DeviceListItem>) {
|
||||
let added = devices.len();
|
||||
|
||||
let prev_devices = self.imp().list.replace(devices);
|
||||
|
@ -155,7 +127,9 @@ impl DeviceList {
|
|||
&self,
|
||||
response: Result<(Option<MatrixDevice>, Vec<MatrixDevice>, CryptoDevices), Error>,
|
||||
) {
|
||||
let session = self.session();
|
||||
let Some(session) = self.session() else {
|
||||
return;
|
||||
};
|
||||
|
||||
match response {
|
||||
Ok((current_device, devices, crypto_devices)) => {
|
||||
|
@ -163,7 +137,7 @@ impl DeviceList {
|
|||
.into_iter()
|
||||
.map(|device| {
|
||||
let crypto_device = crypto_devices.get(&device.device_id);
|
||||
DeviceItem::for_device(Device::new(&session, device, crypto_device))
|
||||
DeviceListItem::for_device(Device::new(&session, device, crypto_device))
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
@ -171,12 +145,12 @@ impl DeviceList {
|
|||
|
||||
self.set_current_device(current_device.map(|device| {
|
||||
let crypto_device = crypto_devices.get(&device.device_id);
|
||||
DeviceItem::for_device(Device::new(&session, device, crypto_device))
|
||||
DeviceListItem::for_device(Device::new(&session, device, crypto_device))
|
||||
}));
|
||||
}
|
||||
Err(error) => {
|
||||
error!("Couldn’t load device list: {error}");
|
||||
self.update_list(vec![DeviceItem::for_error(gettext(
|
||||
self.update_list(vec![DeviceListItem::for_error(gettext(
|
||||
"Failed to load the list of connected devices.",
|
||||
))]);
|
||||
}
|
||||
|
@ -185,7 +159,10 @@ impl DeviceList {
|
|||
}
|
||||
|
||||
pub fn load_devices(&self) {
|
||||
let client = self.session().client();
|
||||
let Some(session) = self.session() else {
|
||||
return;
|
||||
};
|
||||
let client = session.client();
|
||||
|
||||
self.set_loading(true);
|
||||
|
||||
|
|
|
@ -15,14 +15,14 @@ mod imp {
|
|||
use std::cell::{Cell, RefCell};
|
||||
|
||||
use glib::subclass::InitializingObject;
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default, CompositeTemplate)]
|
||||
#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
|
||||
#[template(
|
||||
resource = "/org/gnome/Fractal/ui/session/view/account_settings/devices_page/device_row.ui"
|
||||
)]
|
||||
#[properties(wrapper_type = super::DeviceRow)]
|
||||
pub struct DeviceRow {
|
||||
#[template_child]
|
||||
pub display_name: TemplateChild<gtk::Label>,
|
||||
|
@ -36,7 +36,11 @@ mod imp {
|
|||
pub delete_logout_button: TemplateChild<SpinnerButton>,
|
||||
#[template_child]
|
||||
pub verify_button: TemplateChild<SpinnerButton>,
|
||||
/// The device displayed by this row.
|
||||
#[property(get, set = Self::set_device, construct_only)]
|
||||
pub device: RefCell<Option<Device>>,
|
||||
/// Whether this is the device of the current session.
|
||||
#[property(get, construct_only)]
|
||||
pub is_current_device: Cell<bool>,
|
||||
pub system_settings_handler: RefCell<Option<glib::SignalHandlerId>>,
|
||||
}
|
||||
|
@ -56,46 +60,8 @@ mod imp {
|
|||
}
|
||||
}
|
||||
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for DeviceRow {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![
|
||||
glib::ParamSpecObject::builder::<Device>("device")
|
||||
.construct_only()
|
||||
.build(),
|
||||
glib::ParamSpecBoolean::builder("is-current-device")
|
||||
.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() {
|
||||
"device" => {
|
||||
obj.set_device(value.get().unwrap());
|
||||
}
|
||||
"is-current-device" => {
|
||||
obj.set_current_device(value.get().unwrap());
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
let obj = self.obj();
|
||||
|
||||
match pspec.name() {
|
||||
"device" => obj.device().to_value(),
|
||||
"is-current-device" => obj.is_current_device().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
let obj = self.obj();
|
||||
|
@ -144,9 +110,38 @@ mod imp {
|
|||
|
||||
impl WidgetImpl for DeviceRow {}
|
||||
impl ListBoxRowImpl for DeviceRow {}
|
||||
|
||||
impl DeviceRow {
|
||||
/// Set the device displayed by this row.
|
||||
fn set_device(&self, device: Device) {
|
||||
let obj = self.obj();
|
||||
|
||||
self.display_name.set_label(&device.display_name());
|
||||
obj.set_tooltip_text(Some(device.device_id().as_str()));
|
||||
|
||||
self.verified_icon.set_visible(device.verified());
|
||||
// TODO: Implement verification
|
||||
// imp.verify_button.set_visible(!device.is_verified());
|
||||
|
||||
let last_seen_ip = device.last_seen_ip();
|
||||
if let Some(last_seen_ip) = &last_seen_ip {
|
||||
self.last_seen_ip.set_label(last_seen_ip);
|
||||
}
|
||||
self.last_seen_ip.set_visible(last_seen_ip.is_some());
|
||||
|
||||
self.last_seen_ts
|
||||
.set_visible(device.last_seen_ts().is_some());
|
||||
|
||||
self.device.replace(Some(device));
|
||||
obj.notify_device();
|
||||
|
||||
obj.update_last_seen_ts();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
/// A row presenting a device.
|
||||
pub struct DeviceRow(ObjectSubclass<imp::DeviceRow>)
|
||||
@extends gtk::Widget, gtk::ListBoxRow, @implements gtk::Accessible;
|
||||
}
|
||||
|
@ -159,60 +154,6 @@ impl DeviceRow {
|
|||
.build()
|
||||
}
|
||||
|
||||
/// The device displayed by this row.
|
||||
pub fn device(&self) -> Option<Device> {
|
||||
self.imp().device.borrow().clone()
|
||||
}
|
||||
|
||||
/// Set the device displayed by this row.
|
||||
pub fn set_device(&self, device: Option<Device>) {
|
||||
let imp = self.imp();
|
||||
|
||||
if self.device() == device {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(ref device) = device {
|
||||
imp.display_name.set_label(device.display_name());
|
||||
self.set_tooltip_text(Some(device.device_id().as_str()));
|
||||
|
||||
imp.verified_icon.set_visible(device.is_verified());
|
||||
// TODO: Implement verification
|
||||
// imp.verify_button.set_visible(!device.is_verified());
|
||||
|
||||
let last_seen_ip_visible = if let Some(last_seen_ip) = device.last_seen_ip() {
|
||||
imp.last_seen_ip.set_label(last_seen_ip);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
imp.last_seen_ip.set_visible(last_seen_ip_visible);
|
||||
|
||||
imp.last_seen_ts
|
||||
.set_visible(device.last_seen_ts().is_some());
|
||||
}
|
||||
|
||||
imp.device.replace(device);
|
||||
self.notify("device");
|
||||
|
||||
self.update_last_seen_ts();
|
||||
}
|
||||
|
||||
/// Set whether this is the device of the current session.
|
||||
fn set_current_device(&self, input_bool: bool) {
|
||||
let imp = self.imp();
|
||||
if imp.is_current_device.get() == input_bool {
|
||||
return;
|
||||
}
|
||||
imp.is_current_device.replace(input_bool);
|
||||
self.notify("is-current-device");
|
||||
}
|
||||
|
||||
/// Whether this is the device of the current session.
|
||||
pub fn is_current_device(&self) -> bool {
|
||||
self.imp().is_current_device.get()
|
||||
}
|
||||
|
||||
fn delete(&self) {
|
||||
self.imp().delete_logout_button.set_loading(true);
|
||||
|
||||
|
@ -229,7 +170,7 @@ impl DeviceRow {
|
|||
error!("Failed to disconnect device {}: {error:?}", device.device_id());
|
||||
let device_name = device.display_name();
|
||||
// Translators: Do NOT translate the content between '{' and '}', this is a variable name.
|
||||
let error_message = gettext_f("Failed to disconnect device “{device_name}”", &[("device_name", device_name)]);
|
||||
let error_message = gettext_f("Failed to disconnect device “{device_name}”", &[("device_name", &device_name)]);
|
||||
toast!(obj, error_message);
|
||||
},
|
||||
}
|
||||
|
|
|
@ -2,13 +2,16 @@ use adw::subclass::prelude::*;
|
|||
use gtk::{glib, glib::clone, prelude::*, CompositeTemplate};
|
||||
|
||||
mod device;
|
||||
use self::device::Device;
|
||||
mod device_row;
|
||||
use self::device_row::DeviceRow;
|
||||
mod device_item;
|
||||
use self::device_item::Item as DeviceItem;
|
||||
mod device_list;
|
||||
use self::device_list::DeviceList;
|
||||
mod device_row;
|
||||
|
||||
use self::{
|
||||
device::Device,
|
||||
device_item::{DeviceListItem, DeviceListItemType},
|
||||
device_list::DeviceList,
|
||||
device_row::DeviceRow,
|
||||
};
|
||||
use crate::{components::LoadingRow, session::model::User};
|
||||
|
||||
mod imp {
|
||||
|
@ -18,11 +21,14 @@ mod imp {
|
|||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default, CompositeTemplate)]
|
||||
#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
|
||||
#[template(
|
||||
resource = "/org/gnome/Fractal/ui/session/view/account_settings/devices_page/mod.ui"
|
||||
)]
|
||||
#[properties(wrapper_type = super::DevicesPage)]
|
||||
pub struct DevicesPage {
|
||||
/// The logged-in user.
|
||||
#[property(get, set = Self::set_user, explicit_notify)]
|
||||
pub user: RefCell<Option<User>>,
|
||||
#[template_child]
|
||||
pub other_sessions_group: TemplateChild<adw::PreferencesGroup>,
|
||||
|
@ -47,32 +53,67 @@ mod imp {
|
|||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for DevicesPage {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
use once_cell::sync::Lazy;
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> =
|
||||
Lazy::new(|| vec![glib::ParamSpecObject::builder::<User>("user").build()]);
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
match pspec.name() {
|
||||
"user" => self.obj().set_user(value.get().unwrap()),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
match pspec.name() {
|
||||
"user" => self.obj().user().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for DevicesPage {}
|
||||
|
||||
impl WidgetImpl for DevicesPage {}
|
||||
impl PreferencesPageImpl for DevicesPage {}
|
||||
|
||||
impl DevicesPage {
|
||||
/// Set the logged-in user.
|
||||
fn set_user(&self, user: Option<User>) {
|
||||
if *self.user.borrow() == user {
|
||||
return;
|
||||
}
|
||||
let obj = self.obj();
|
||||
|
||||
if let Some(user) = &user {
|
||||
let device_list = DeviceList::new(&user.session());
|
||||
self.other_sessions.bind_model(
|
||||
Some(&device_list),
|
||||
clone!(@weak device_list => @default-panic, move |item| {
|
||||
match item.downcast_ref::<DeviceListItem>().unwrap().item_type() {
|
||||
DeviceListItemType::Device(device) => DeviceRow::new(&device, false).upcast(),
|
||||
DeviceListItemType::Error(error) => {
|
||||
let row = LoadingRow::new();
|
||||
row.set_error(Some(error.clone()));
|
||||
row.connect_retry(clone!(@weak device_list => move|_| {
|
||||
device_list.load_devices()
|
||||
}));
|
||||
row.upcast()
|
||||
}
|
||||
DeviceListItemType::LoadingSpinner => {
|
||||
LoadingRow::new().upcast()
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
device_list.connect_items_changed(
|
||||
clone!(@weak obj => move |device_list, _, _, _| {
|
||||
obj.set_other_sessions_visibility(device_list.n_items() > 0)
|
||||
}),
|
||||
);
|
||||
|
||||
obj.set_other_sessions_visibility(device_list.n_items() > 0);
|
||||
|
||||
device_list.connect_current_device_notify(clone!(@weak obj => move |device_list| {
|
||||
obj.set_current_device(device_list);
|
||||
}));
|
||||
|
||||
obj.set_current_device(&device_list);
|
||||
} else {
|
||||
self.other_sessions.unbind_model();
|
||||
|
||||
if let Some(child) = self.current_session.first_child() {
|
||||
self.current_session.remove(&child);
|
||||
}
|
||||
}
|
||||
|
||||
self.user.replace(user);
|
||||
obj.notify_user();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
|
@ -86,69 +127,6 @@ impl DevicesPage {
|
|||
glib::Object::builder().property("user", user).build()
|
||||
}
|
||||
|
||||
/// The logged-in user.
|
||||
pub fn user(&self) -> Option<User> {
|
||||
self.imp().user.borrow().clone()
|
||||
}
|
||||
|
||||
/// Set the logged-in user.
|
||||
fn set_user(&self, user: Option<User>) {
|
||||
let imp = self.imp();
|
||||
|
||||
if self.user() == user {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(ref user) = user {
|
||||
let device_list = DeviceList::new(&user.session());
|
||||
imp.other_sessions.bind_model(
|
||||
Some(&device_list),
|
||||
clone!(@weak device_list => @default-panic, move |item| {
|
||||
match item.downcast_ref::<DeviceItem>().unwrap().type_() {
|
||||
device_item::ItemType::Device(device) => DeviceRow::new(device, false).upcast(),
|
||||
device_item::ItemType::Error(error) => {
|
||||
let row = LoadingRow::new();
|
||||
row.set_error(Some(error.clone()));
|
||||
row.connect_retry(clone!(@weak device_list => move|_| {
|
||||
device_list.load_devices()
|
||||
}));
|
||||
row.upcast()
|
||||
}
|
||||
device_item::ItemType::LoadingSpinner => {
|
||||
LoadingRow::new().upcast()
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
device_list.connect_items_changed(
|
||||
clone!(@weak self as obj => move |device_list, _, _, _| {
|
||||
obj.set_other_sessions_visibility(device_list.n_items() > 0)
|
||||
}),
|
||||
);
|
||||
|
||||
self.set_other_sessions_visibility(device_list.n_items() > 0);
|
||||
|
||||
device_list.connect_notify_local(
|
||||
Some("current-device"),
|
||||
clone!(@weak self as obj => move |device_list, _| {
|
||||
obj.set_current_device(device_list);
|
||||
}),
|
||||
);
|
||||
|
||||
self.set_current_device(&device_list);
|
||||
} else {
|
||||
imp.other_sessions.unbind_model();
|
||||
|
||||
if let Some(child) = imp.current_session.first_child() {
|
||||
imp.current_session.remove(&child);
|
||||
}
|
||||
}
|
||||
|
||||
imp.user.replace(user);
|
||||
self.notify("user");
|
||||
}
|
||||
|
||||
fn set_other_sessions_visibility(&self, visible: bool) {
|
||||
self.imp().other_sessions_group.set_visible(visible);
|
||||
}
|
||||
|
@ -158,9 +136,9 @@ impl DevicesPage {
|
|||
if let Some(child) = imp.current_session.first_child() {
|
||||
imp.current_session.remove(&child);
|
||||
}
|
||||
let row: gtk::Widget = match device_list.current_device().type_() {
|
||||
device_item::ItemType::Device(device) => DeviceRow::new(device, true).upcast(),
|
||||
device_item::ItemType::Error(error) => {
|
||||
let row: gtk::Widget = match device_list.current_device().item_type() {
|
||||
DeviceListItemType::Device(device) => DeviceRow::new(&device, true).upcast(),
|
||||
DeviceListItemType::Error(error) => {
|
||||
let row = LoadingRow::new();
|
||||
row.set_error(Some(error.clone()));
|
||||
row.connect_retry(clone!(@weak device_list => move|_| {
|
||||
|
@ -168,7 +146,7 @@ impl DevicesPage {
|
|||
}));
|
||||
row.upcast()
|
||||
}
|
||||
device_item::ItemType::LoadingSpinner => LoadingRow::new().upcast(),
|
||||
DeviceListItemType::LoadingSpinner => LoadingRow::new().upcast(),
|
||||
};
|
||||
imp.current_session.append(&row);
|
||||
}
|
||||
|
|
|
@ -18,16 +18,19 @@ use crate::{
|
|||
};
|
||||
|
||||
mod imp {
|
||||
use glib::{subclass::InitializingObject, WeakRef};
|
||||
use glib::subclass::InitializingObject;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default, CompositeTemplate)]
|
||||
#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
|
||||
#[template(
|
||||
resource = "/org/gnome/Fractal/ui/session/view/account_settings/general_page/change_password_subpage.ui"
|
||||
)]
|
||||
#[properties(wrapper_type = super::ChangePasswordSubpage)]
|
||||
pub struct ChangePasswordSubpage {
|
||||
pub session: WeakRef<Session>,
|
||||
/// The current session.
|
||||
#[property(get, set, nullable)]
|
||||
pub session: glib::WeakRef<Session>,
|
||||
#[template_child]
|
||||
pub password: TemplateChild<adw::PasswordEntryRow>,
|
||||
#[template_child]
|
||||
|
@ -62,29 +65,8 @@ mod imp {
|
|||
}
|
||||
}
|
||||
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for ChangePasswordSubpage {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
use once_cell::sync::Lazy;
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> =
|
||||
Lazy::new(|| vec![glib::ParamSpecObject::builder::<Session>("session").build()]);
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
match pspec.name() {
|
||||
"session" => self.obj().set_session(value.get().unwrap()),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
match pspec.name() {
|
||||
"session" => self.obj().session().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
let obj = self.obj();
|
||||
|
@ -127,16 +109,6 @@ impl ChangePasswordSubpage {
|
|||
glib::Object::builder().property("session", session).build()
|
||||
}
|
||||
|
||||
/// The current session.
|
||||
pub fn session(&self) -> Option<Session> {
|
||||
self.imp().session.upgrade()
|
||||
}
|
||||
|
||||
/// Set the current session.
|
||||
pub fn set_session(&self, session: Option<Session>) {
|
||||
self.imp().session.set(session.as_ref());
|
||||
}
|
||||
|
||||
fn validate_password(&self) {
|
||||
let imp = self.imp();
|
||||
let entry = &imp.password;
|
||||
|
@ -239,6 +211,10 @@ impl ChangePasswordSubpage {
|
|||
}
|
||||
|
||||
async fn change_password(&self) {
|
||||
let Some(session) = self.session() else {
|
||||
return;
|
||||
};
|
||||
|
||||
if !self.can_change_password() {
|
||||
return;
|
||||
}
|
||||
|
@ -250,7 +226,6 @@ impl ChangePasswordSubpage {
|
|||
imp.password.set_sensitive(false);
|
||||
imp.confirm_password.set_sensitive(false);
|
||||
|
||||
let session = self.session().unwrap();
|
||||
let dialog = AuthDialog::new(self.root().and_downcast_ref::<gtk::Window>(), &session);
|
||||
|
||||
let result = dialog
|
||||
|
|
|
@ -15,16 +15,19 @@ use crate::{
|
|||
};
|
||||
|
||||
mod imp {
|
||||
use glib::{subclass::InitializingObject, WeakRef};
|
||||
use glib::subclass::InitializingObject;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default, CompositeTemplate)]
|
||||
#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
|
||||
#[template(
|
||||
resource = "/org/gnome/Fractal/ui/session/view/account_settings/general_page/deactivate_account_subpage.ui"
|
||||
)]
|
||||
#[properties(wrapper_type = super::DeactivateAccountSubpage)]
|
||||
pub struct DeactivateAccountSubpage {
|
||||
pub session: WeakRef<Session>,
|
||||
/// The current session.
|
||||
#[property(get, set = Self::set_session, nullable)]
|
||||
pub session: glib::WeakRef<Session>,
|
||||
#[template_child]
|
||||
pub confirmation: TemplateChild<adw::EntryRow>,
|
||||
#[template_child]
|
||||
|
@ -46,29 +49,8 @@ mod imp {
|
|||
}
|
||||
}
|
||||
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for DeactivateAccountSubpage {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
use once_cell::sync::Lazy;
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> =
|
||||
Lazy::new(|| vec![glib::ParamSpecObject::builder::<Session>("session").build()]);
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
match pspec.name() {
|
||||
"session" => self.obj().set_session(value.get().unwrap()),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
match pspec.name() {
|
||||
"session" => self.obj().session().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
let obj = self.obj();
|
||||
|
@ -98,6 +80,16 @@ mod imp {
|
|||
|
||||
impl WidgetImpl for DeactivateAccountSubpage {}
|
||||
impl NavigationPageImpl for DeactivateAccountSubpage {}
|
||||
|
||||
impl DeactivateAccountSubpage {
|
||||
/// Set the current session.
|
||||
fn set_session(&self, session: Option<Session>) {
|
||||
if let Some(session) = session {
|
||||
self.session.set(Some(&session));
|
||||
self.confirmation.set_title(session.user_id().as_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
|
@ -111,27 +103,6 @@ impl DeactivateAccountSubpage {
|
|||
glib::Object::builder().property("session", session).build()
|
||||
}
|
||||
|
||||
/// The current session.
|
||||
pub fn session(&self) -> Option<Session> {
|
||||
self.imp().session.upgrade()
|
||||
}
|
||||
|
||||
/// Set the current session.
|
||||
pub fn set_session(&self, session: Option<Session>) {
|
||||
if let Some(session) = session {
|
||||
let imp = self.imp();
|
||||
imp.session.set(Some(&session));
|
||||
imp.confirmation.set_title(&self.user_id());
|
||||
}
|
||||
}
|
||||
|
||||
fn user_id(&self) -> String {
|
||||
self.session()
|
||||
.as_ref()
|
||||
.map(|session| session.user_id().to_string())
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn update_button(&self) {
|
||||
self.imp()
|
||||
.button
|
||||
|
@ -139,11 +110,15 @@ impl DeactivateAccountSubpage {
|
|||
}
|
||||
|
||||
fn can_deactivate_account(&self) -> bool {
|
||||
let confirmation = self.imp().confirmation.text();
|
||||
confirmation == self.user_id()
|
||||
let confirmation = &self.imp().confirmation;
|
||||
confirmation.text() == confirmation.title()
|
||||
}
|
||||
|
||||
async fn deactivate_account(&self) {
|
||||
let Some(session) = self.session() else {
|
||||
return;
|
||||
};
|
||||
|
||||
if !self.can_deactivate_account() {
|
||||
return;
|
||||
}
|
||||
|
@ -152,7 +127,6 @@ impl DeactivateAccountSubpage {
|
|||
imp.button.set_loading(true);
|
||||
imp.confirmation.set_sensitive(false);
|
||||
|
||||
let session = self.session().unwrap();
|
||||
let dialog = AuthDialog::new(self.root().and_downcast_ref::<gtk::Window>(), &session);
|
||||
|
||||
let result = dialog
|
||||
|
|
|
@ -7,16 +7,19 @@ use gtk::{
|
|||
use crate::{components::SpinnerButton, session::model::Session, spawn, toast};
|
||||
|
||||
mod imp {
|
||||
use glib::{subclass::InitializingObject, WeakRef};
|
||||
use glib::subclass::InitializingObject;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default, CompositeTemplate)]
|
||||
#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
|
||||
#[template(
|
||||
resource = "/org/gnome/Fractal/ui/session/view/account_settings/general_page/log_out_subpage.ui"
|
||||
)]
|
||||
#[properties(wrapper_type = super::LogOutSubpage)]
|
||||
pub struct LogOutSubpage {
|
||||
pub session: WeakRef<Session>,
|
||||
/// The current session.
|
||||
#[property(get, set, nullable)]
|
||||
pub session: glib::WeakRef<Session>,
|
||||
#[template_child]
|
||||
pub logout_button: TemplateChild<SpinnerButton>,
|
||||
#[template_child]
|
||||
|
@ -39,29 +42,8 @@ mod imp {
|
|||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for LogOutSubpage {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
use once_cell::sync::Lazy;
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> =
|
||||
Lazy::new(|| vec![glib::ParamSpecObject::builder::<Session>("session").build()]);
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
match pspec.name() {
|
||||
"session" => self.obj().set_session(value.get().unwrap()),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
match pspec.name() {
|
||||
"session" => self.obj().session().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for LogOutSubpage {}
|
||||
|
||||
impl WidgetImpl for LogOutSubpage {}
|
||||
impl NavigationPageImpl for LogOutSubpage {}
|
||||
|
@ -79,37 +61,24 @@ impl LogOutSubpage {
|
|||
glib::Object::builder().property("session", session).build()
|
||||
}
|
||||
|
||||
/// The current session.
|
||||
pub fn session(&self) -> Option<Session> {
|
||||
self.imp().session.upgrade()
|
||||
}
|
||||
|
||||
/// Set the current session.
|
||||
pub fn set_session(&self, session: Option<Session>) {
|
||||
if let Some(session) = session {
|
||||
self.imp().session.set(Some(&session));
|
||||
}
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn logout_button_clicked_cb(&self) {
|
||||
let Some(session) = self.session() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let imp = self.imp();
|
||||
let logout_button = imp.logout_button.get();
|
||||
let make_backup_button = imp.make_backup_button.get();
|
||||
let session = self.session().unwrap();
|
||||
imp.logout_button.set_loading(true);
|
||||
imp.make_backup_button.set_sensitive(false);
|
||||
|
||||
logout_button.set_loading(true);
|
||||
make_backup_button.set_sensitive(false);
|
||||
spawn!(clone!(@weak self as obj, @weak session => async move {
|
||||
if let Err(error) = session.logout().await {
|
||||
toast!(obj, error);
|
||||
}
|
||||
|
||||
spawn!(
|
||||
clone!(@weak self as obj, @weak logout_button, @weak make_backup_button, @weak session => async move {
|
||||
if let Err(error) = session.logout().await {
|
||||
toast!(obj, error);
|
||||
}
|
||||
|
||||
logout_button.set_loading(false);
|
||||
make_backup_button.set_sensitive(true);
|
||||
})
|
||||
);
|
||||
let imp = obj.imp();
|
||||
imp.logout_button.set_loading(false);
|
||||
imp.make_backup_button.set_sensitive(true);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ use log_out_subpage::LogOutSubpage;
|
|||
use crate::{
|
||||
components::{ActionButton, ActionState, ButtonRow, EditableAvatar},
|
||||
prelude::*,
|
||||
session::model::{Session, User},
|
||||
session::model::Session,
|
||||
spawn, spawn_tokio, toast,
|
||||
utils::{media::load_file, template_callbacks::TemplateCallbacks, OngoingAsyncAction},
|
||||
};
|
||||
|
@ -27,16 +27,19 @@ use crate::{
|
|||
mod imp {
|
||||
use std::cell::RefCell;
|
||||
|
||||
use glib::{subclass::InitializingObject, WeakRef};
|
||||
use glib::subclass::InitializingObject;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default, CompositeTemplate)]
|
||||
#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
|
||||
#[template(
|
||||
resource = "/org/gnome/Fractal/ui/session/view/account_settings/general_page/mod.ui"
|
||||
)]
|
||||
#[properties(wrapper_type = super::GeneralPage)]
|
||||
pub struct GeneralPage {
|
||||
pub session: WeakRef<Session>,
|
||||
/// The current session.
|
||||
#[property(get, set = Self::set_session, nullable)]
|
||||
pub session: glib::WeakRef<Session>,
|
||||
#[template_child]
|
||||
pub avatar: TemplateChild<EditableAvatar>,
|
||||
#[template_child]
|
||||
|
@ -59,6 +62,8 @@ mod imp {
|
|||
pub log_out_subpage: TemplateChild<LogOutSubpage>,
|
||||
pub changing_avatar: RefCell<Option<OngoingAsyncAction<String>>>,
|
||||
pub changing_display_name: RefCell<Option<OngoingAsyncAction<String>>>,
|
||||
pub avatar_uri_handler: RefCell<Option<glib::SignalHandlerId>>,
|
||||
pub display_name_handler: RefCell<Option<glib::SignalHandlerId>>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
|
@ -98,32 +103,8 @@ mod imp {
|
|||
}
|
||||
}
|
||||
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for GeneralPage {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
use once_cell::sync::Lazy;
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![glib::ParamSpecObject::builder::<Session>("session")
|
||||
.explicit_notify()
|
||||
.build()]
|
||||
});
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
match pspec.name() {
|
||||
"session" => self.obj().set_session(value.get().unwrap()),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
match pspec.name() {
|
||||
"session" => self.obj().session().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
let obj = self.obj();
|
||||
|
@ -136,6 +117,54 @@ mod imp {
|
|||
|
||||
impl WidgetImpl for GeneralPage {}
|
||||
impl PreferencesPageImpl for GeneralPage {}
|
||||
|
||||
impl GeneralPage {
|
||||
/// Set the current session.
|
||||
fn set_session(&self, session: Option<Session>) {
|
||||
let prev_session = self.session.upgrade();
|
||||
if prev_session == session {
|
||||
return;
|
||||
}
|
||||
let obj = self.obj();
|
||||
|
||||
if let Some(session) = prev_session {
|
||||
let user = session.user();
|
||||
|
||||
if let Some(handler) = self.avatar_uri_handler.take() {
|
||||
user.avatar_data().image().unwrap().disconnect(handler)
|
||||
}
|
||||
if let Some(handler) = self.display_name_handler.take() {
|
||||
user.disconnect(handler);
|
||||
}
|
||||
}
|
||||
|
||||
self.session.set(session.as_ref());
|
||||
obj.notify_session();
|
||||
|
||||
let Some(session) = session else {
|
||||
return;
|
||||
};
|
||||
|
||||
self.user_id.set_subtitle(session.user_id().as_str());
|
||||
self.homeserver.set_subtitle(session.homeserver().as_str());
|
||||
self.session_id.set_subtitle(session.device_id().as_str());
|
||||
|
||||
let user = session.user();
|
||||
let avatar_uri_handler = user.avatar_data().image().unwrap().connect_uri_notify(
|
||||
clone!(@weak obj => move |avatar_image| {
|
||||
obj.avatar_changed(avatar_image.uri());
|
||||
}),
|
||||
);
|
||||
self.avatar_uri_handler.replace(Some(avatar_uri_handler));
|
||||
|
||||
let display_name_handler =
|
||||
user.connect_display_name_notify(clone!(@weak obj => move |user| {
|
||||
obj.display_name_changed(user.display_name());
|
||||
}));
|
||||
self.display_name_handler
|
||||
.replace(Some(display_name_handler));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
|
@ -150,58 +179,6 @@ impl GeneralPage {
|
|||
glib::Object::builder().property("session", session).build()
|
||||
}
|
||||
|
||||
/// The current session.
|
||||
pub fn session(&self) -> Option<Session> {
|
||||
self.imp().session.upgrade()
|
||||
}
|
||||
|
||||
/// Set the current session.
|
||||
pub fn set_session(&self, session: Option<Session>) {
|
||||
if self.session() == session {
|
||||
return;
|
||||
}
|
||||
self.imp().session.set(session.as_ref());
|
||||
self.notify("session");
|
||||
|
||||
self.user()
|
||||
.avatar_data()
|
||||
.image()
|
||||
.unwrap()
|
||||
.connect_uri_notify(clone!(@weak self as obj => move |avatar_image| {
|
||||
obj.avatar_changed(avatar_image.uri());
|
||||
}));
|
||||
self.user().connect_notify_local(
|
||||
Some("display-name"),
|
||||
clone!(@weak self as obj => move |user, _| {
|
||||
obj.display_name_changed(user.display_name());
|
||||
}),
|
||||
);
|
||||
|
||||
spawn!(
|
||||
glib::Priority::LOW,
|
||||
clone!(@weak self as obj => async move {
|
||||
let imp = obj.imp();
|
||||
let client = obj.session().unwrap().client();
|
||||
|
||||
let homeserver = client.homeserver();
|
||||
imp.homeserver.set_subtitle(homeserver.as_ref());
|
||||
|
||||
let user_id = client.user_id().unwrap();
|
||||
imp.user_id.set_subtitle(user_id.as_ref());
|
||||
|
||||
let session_id = client.device_id().unwrap();
|
||||
imp.session_id.set_subtitle(session_id.as_ref());
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
fn user(&self) -> User {
|
||||
self.session()
|
||||
.as_ref()
|
||||
.map(|session| session.user())
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn init_avatar(&self) {
|
||||
let avatar = &self.imp().avatar;
|
||||
avatar.connect_edit_avatar(clone!(@weak self as obj => move |_, file| {
|
||||
|
@ -245,6 +222,10 @@ impl GeneralPage {
|
|||
}
|
||||
|
||||
async fn change_avatar(&self, file: gio::File) {
|
||||
let Some(session) = self.session() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let imp = self.imp();
|
||||
let avatar = &imp.avatar;
|
||||
avatar.edit_in_progress();
|
||||
|
@ -259,7 +240,7 @@ impl GeneralPage {
|
|||
}
|
||||
};
|
||||
|
||||
let client = self.session().unwrap().client();
|
||||
let client = session.client();
|
||||
let client_clone = client.clone();
|
||||
let handle =
|
||||
spawn_tokio!(async move { client_clone.media().upload(&info.mime, data).await });
|
||||
|
@ -288,7 +269,7 @@ impl GeneralPage {
|
|||
// Because this action can finish in avatar_changed, we must only act if this is
|
||||
// still the current action.
|
||||
if weak_action.is_ongoing() {
|
||||
self.user().set_avatar_url(Some(uri))
|
||||
session.user().set_avatar_url(Some(uri))
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
|
@ -305,6 +286,9 @@ impl GeneralPage {
|
|||
}
|
||||
|
||||
async fn remove_avatar(&self) {
|
||||
let Some(session) = self.session() else {
|
||||
return;
|
||||
};
|
||||
let Some(window) = self.root().and_downcast::<gtk::Window>() else {
|
||||
return;
|
||||
};
|
||||
|
@ -333,7 +317,7 @@ impl GeneralPage {
|
|||
let (action, weak_action) = OngoingAsyncAction::remove();
|
||||
imp.changing_avatar.replace(Some(action));
|
||||
|
||||
let client = self.session().unwrap().client();
|
||||
let client = session.client();
|
||||
let handle = spawn_tokio!(async move { client.account().set_avatar_url(None).await });
|
||||
|
||||
match handle.await.unwrap() {
|
||||
|
@ -343,7 +327,7 @@ impl GeneralPage {
|
|||
// Because this action can finish in avatar_changed, we must only act if this is
|
||||
// still the current action.
|
||||
if weak_action.is_ongoing() {
|
||||
self.user().set_avatar_url(None)
|
||||
session.user().set_avatar_url(None)
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
|
@ -362,8 +346,12 @@ impl GeneralPage {
|
|||
fn init_display_name(&self) {
|
||||
let imp = self.imp();
|
||||
let entry = &imp.display_name;
|
||||
entry.connect_changed(clone!(@weak self as obj => move|entry| {
|
||||
obj.imp().display_name_button.set_visible(entry.text() != obj.user().display_name());
|
||||
entry.connect_changed(clone!(@weak self as obj => move |entry| {
|
||||
let Some(session) = obj.session() else {
|
||||
return;
|
||||
};
|
||||
|
||||
obj.imp().display_name_button.set_visible(entry.text() != session.user().display_name());
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -395,6 +383,10 @@ impl GeneralPage {
|
|||
}
|
||||
|
||||
async fn change_display_name(&self) {
|
||||
let Some(session) = self.session() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let imp = self.imp();
|
||||
let entry = &imp.display_name;
|
||||
let button = &imp.display_name_button;
|
||||
|
@ -407,7 +399,7 @@ impl GeneralPage {
|
|||
let (action, weak_action) = OngoingAsyncAction::set(display_name.clone());
|
||||
imp.changing_display_name.replace(Some(action));
|
||||
|
||||
let client = self.session().unwrap().client();
|
||||
let client = session.client();
|
||||
let display_name_clone = display_name.clone();
|
||||
let handle = spawn_tokio!(async move {
|
||||
client
|
||||
|
@ -423,7 +415,7 @@ impl GeneralPage {
|
|||
// Because this action can finish in display_name_changed, we must only act if
|
||||
// this is still the current action.
|
||||
if weak_action.is_ongoing() {
|
||||
self.user().set_display_name(Some(display_name));
|
||||
session.user().set_display_name(Some(display_name));
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
|
@ -442,11 +434,14 @@ impl GeneralPage {
|
|||
}
|
||||
|
||||
fn init_change_password(&self) {
|
||||
let Some(session) = self.session() else {
|
||||
return;
|
||||
};
|
||||
let client = session.client();
|
||||
|
||||
spawn!(
|
||||
glib::Priority::LOW,
|
||||
clone!(@weak self as obj => async move {
|
||||
let client = obj.session().unwrap().client();
|
||||
|
||||
// Check whether the user can change their password.
|
||||
let handle = spawn_tokio!(async move {
|
||||
client.send(get_capabilities::v3::Request::new(), None).await
|
||||
|
|
|
@ -14,19 +14,22 @@ use self::{
|
|||
devices_page::DevicesPage, general_page::GeneralPage, notifications_page::NotificationsPage,
|
||||
security_page::SecurityPage,
|
||||
};
|
||||
use crate::session::model::Session;
|
||||
use crate::{session::model::Session, utils::BoundObjectWeakRef};
|
||||
|
||||
mod imp {
|
||||
use std::cell::RefCell;
|
||||
|
||||
use glib::{subclass::InitializingObject, WeakRef};
|
||||
use glib::subclass::InitializingObject;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default, CompositeTemplate)]
|
||||
#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
|
||||
#[template(resource = "/org/gnome/Fractal/ui/session/view/account_settings/mod.ui")]
|
||||
#[properties(wrapper_type = super::AccountSettings)]
|
||||
pub struct AccountSettings {
|
||||
pub session: WeakRef<Session>,
|
||||
/// The current session.
|
||||
#[property(get, set = Self::set_session, nullable)]
|
||||
pub session: BoundObjectWeakRef<Session>,
|
||||
pub session_handler: RefCell<Option<glib::SignalHandlerId>>,
|
||||
#[template_child]
|
||||
pub general_page: TemplateChild<GeneralPage>,
|
||||
|
@ -45,6 +48,7 @@ mod imp {
|
|||
GeneralPage::static_type();
|
||||
NotificationsPage::static_type();
|
||||
SecurityPage::static_type();
|
||||
|
||||
Self::bind_template(klass);
|
||||
|
||||
klass.install_action("account-settings.close", None, |obj, _, _| {
|
||||
|
@ -76,45 +80,34 @@ mod imp {
|
|||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for AccountSettings {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
use once_cell::sync::Lazy;
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![glib::ParamSpecObject::builder::<Session>("session")
|
||||
.explicit_notify()
|
||||
.build()]
|
||||
});
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
match pspec.name() {
|
||||
"session" => self.obj().set_session(value.get().unwrap()),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
match pspec.name() {
|
||||
"session" => self.obj().session().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn dispose(&self) {
|
||||
if let Some(session) = self.session.upgrade() {
|
||||
if let Some(handler) = self.session_handler.take() {
|
||||
session.disconnect(handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for AccountSettings {}
|
||||
|
||||
impl WidgetImpl for AccountSettings {}
|
||||
impl WindowImpl for AccountSettings {}
|
||||
impl AdwWindowImpl for AccountSettings {}
|
||||
impl PreferencesWindowImpl for AccountSettings {}
|
||||
|
||||
impl AccountSettings {
|
||||
/// Set the current session.
|
||||
fn set_session(&self, session: Option<Session>) {
|
||||
if self.session.obj() == session {
|
||||
return;
|
||||
}
|
||||
let obj = self.obj();
|
||||
|
||||
self.session.disconnect_signals();
|
||||
|
||||
if let Some(session) = session {
|
||||
let logged_out_handler = session.connect_logged_out(clone!(@weak obj => move |_| {
|
||||
obj.close();
|
||||
}));
|
||||
self.session.set(&session, vec![logged_out_handler]);
|
||||
}
|
||||
|
||||
obj.notify_session();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
|
@ -130,35 +123,4 @@ impl AccountSettings {
|
|||
.property("session", session)
|
||||
.build()
|
||||
}
|
||||
|
||||
/// The current session.
|
||||
pub fn session(&self) -> Option<Session> {
|
||||
self.imp().session.upgrade()
|
||||
}
|
||||
|
||||
/// Set the current session.
|
||||
pub fn set_session(&self, session: Option<Session>) {
|
||||
let prev_session = self.session();
|
||||
if prev_session == session {
|
||||
return;
|
||||
}
|
||||
|
||||
let imp = self.imp();
|
||||
if let Some(session) = prev_session {
|
||||
if let Some(handler) = imp.session_handler.take() {
|
||||
session.disconnect(handler);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(session) = &session {
|
||||
imp.session_handler.replace(Some(session.connect_logged_out(
|
||||
clone!(@weak self as obj => move |_| {
|
||||
obj.close();
|
||||
}),
|
||||
)));
|
||||
}
|
||||
|
||||
self.imp().session.set(session.as_ref());
|
||||
self.notify("session");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,18 +22,24 @@ pub enum KeysSubpageMode {
|
|||
}
|
||||
|
||||
mod imp {
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
marker::PhantomData,
|
||||
};
|
||||
|
||||
use glib::{subclass::InitializingObject, WeakRef};
|
||||
use glib::subclass::InitializingObject;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default, CompositeTemplate)]
|
||||
#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
|
||||
#[template(
|
||||
resource = "/org/gnome/Fractal/ui/session/view/account_settings/security_page/import_export_keys_subpage.ui"
|
||||
)]
|
||||
#[properties(wrapper_type = super::ImportExportKeysSubpage)]
|
||||
pub struct ImportExportKeysSubpage {
|
||||
pub session: WeakRef<Session>,
|
||||
/// The current session.
|
||||
#[property(get, set, nullable)]
|
||||
pub session: glib::WeakRef<Session>,
|
||||
#[template_child]
|
||||
pub description: TemplateChild<gtk::Label>,
|
||||
#[template_child]
|
||||
|
@ -54,7 +60,14 @@ mod imp {
|
|||
pub file_button: TemplateChild<gtk::Button>,
|
||||
#[template_child]
|
||||
pub proceed_button: TemplateChild<SpinnerButton>,
|
||||
/// The path to export the keys to.
|
||||
#[property(get)]
|
||||
pub file_path: RefCell<Option<gio::File>>,
|
||||
/// The path to export the keys to, as a string.
|
||||
#[property(get = Self::file_path_string)]
|
||||
pub file_path_string: PhantomData<Option<String>>,
|
||||
/// The export/import mode of the subpage.
|
||||
#[property(get, set = Self::set_mode, explicit_notify, builder(KeysSubpageMode::default()))]
|
||||
pub mode: Cell<KeysSubpageMode>,
|
||||
}
|
||||
|
||||
|
@ -74,49 +87,8 @@ mod imp {
|
|||
}
|
||||
}
|
||||
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for ImportExportKeysSubpage {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
use once_cell::sync::Lazy;
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![
|
||||
glib::ParamSpecObject::builder::<Session>("session").build(),
|
||||
glib::ParamSpecString::builder("file-path")
|
||||
.read_only()
|
||||
.build(),
|
||||
glib::ParamSpecEnum::builder::<KeysSubpageMode>("mode")
|
||||
.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() {
|
||||
"session" => obj.set_session(value.get().unwrap()),
|
||||
"mode" => obj.set_mode(value.get().unwrap()),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
let obj = self.obj();
|
||||
|
||||
match pspec.name() {
|
||||
"session" => obj.session().to_value(),
|
||||
"file-path" => obj
|
||||
.file_path()
|
||||
.and_then(|file| file.path())
|
||||
.map(|path| path.to_string_lossy().to_string())
|
||||
.to_value(),
|
||||
"mode" => obj.mode().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
let obj = self.obj();
|
||||
|
@ -137,6 +109,30 @@ mod imp {
|
|||
|
||||
impl WidgetImpl for ImportExportKeysSubpage {}
|
||||
impl NavigationPageImpl for ImportExportKeysSubpage {}
|
||||
|
||||
impl ImportExportKeysSubpage {
|
||||
/// Set the export/import mode of the subpage.
|
||||
fn set_mode(&self, mode: KeysSubpageMode) {
|
||||
if self.mode.get() == mode {
|
||||
return;
|
||||
}
|
||||
let obj = self.obj();
|
||||
|
||||
self.mode.set(mode);
|
||||
obj.update_for_mode();
|
||||
obj.clear();
|
||||
obj.notify_mode();
|
||||
}
|
||||
|
||||
/// The path to export the keys to, as a string.
|
||||
fn file_path_string(&self) -> Option<String> {
|
||||
self.file_path
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.and_then(|file| file.path())
|
||||
.map(|path| path.to_string_lossy().to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
|
@ -151,21 +147,6 @@ impl ImportExportKeysSubpage {
|
|||
glib::Object::builder().property("session", session).build()
|
||||
}
|
||||
|
||||
/// The current session.
|
||||
pub fn session(&self) -> Option<Session> {
|
||||
self.imp().session.upgrade()
|
||||
}
|
||||
|
||||
/// Set the current session.
|
||||
pub fn set_session(&self, session: Option<Session>) {
|
||||
self.imp().session.set(session.as_ref());
|
||||
}
|
||||
|
||||
/// The path to export the keys to.
|
||||
pub fn file_path(&self) -> Option<gio::File> {
|
||||
self.imp().file_path.borrow().clone()
|
||||
}
|
||||
|
||||
/// Set the path to export the keys to.
|
||||
fn set_file_path(&self, path: Option<gio::File>) {
|
||||
let imp = self.imp();
|
||||
|
@ -175,24 +156,8 @@ impl ImportExportKeysSubpage {
|
|||
|
||||
imp.file_path.replace(path);
|
||||
self.update_button();
|
||||
self.notify("file-path");
|
||||
}
|
||||
|
||||
/// The export/import mode of the subpage.
|
||||
pub fn mode(&self) -> KeysSubpageMode {
|
||||
self.imp().mode.get()
|
||||
}
|
||||
|
||||
/// Set the export/import mode of the subpage.
|
||||
pub fn set_mode(&self, mode: KeysSubpageMode) {
|
||||
if self.mode() == mode {
|
||||
return;
|
||||
}
|
||||
|
||||
self.imp().mode.set(mode);
|
||||
self.update_for_mode();
|
||||
self.clear();
|
||||
self.notify("mode");
|
||||
self.notify_file_path();
|
||||
self.notify_file_path_string();
|
||||
}
|
||||
|
||||
fn clear(&self) {
|
||||
|
|
|
@ -104,7 +104,7 @@
|
|||
<child>
|
||||
<object class="AdwActionRow" id="file_row">
|
||||
<property name="title" translatable="yes">File</property>
|
||||
<property name="subtitle" bind-source="ImportExportKeysSubpage" bind-property="file-path" bind-flags="sync-create"/>
|
||||
<property name="subtitle" bind-source="ImportExportKeysSubpage" bind-property="file-path-string" bind-flags="sync-create"/>
|
||||
<child>
|
||||
<object class="GtkButton" id="file_button">
|
||||
<property name="label" translatable="yes">Choose…</property>
|
||||
|
|
|
@ -8,16 +8,19 @@ mod import_export_keys_subpage;
|
|||
use import_export_keys_subpage::{ImportExportKeysSubpage, KeysSubpageMode};
|
||||
|
||||
mod imp {
|
||||
use glib::{subclass::InitializingObject, WeakRef};
|
||||
use glib::subclass::InitializingObject;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default, CompositeTemplate)]
|
||||
#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
|
||||
#[template(
|
||||
resource = "/org/gnome/Fractal/ui/session/view/account_settings/security_page/mod.ui"
|
||||
)]
|
||||
#[properties(wrapper_type = super::SecurityPage)]
|
||||
pub struct SecurityPage {
|
||||
pub session: WeakRef<Session>,
|
||||
/// The current session.
|
||||
#[property(get, set = Self::set_session, nullable)]
|
||||
pub session: glib::WeakRef<Session>,
|
||||
#[template_child]
|
||||
pub import_export_keys_subpage: TemplateChild<ImportExportKeysSubpage>,
|
||||
#[template_child]
|
||||
|
@ -45,35 +48,28 @@ mod imp {
|
|||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for SecurityPage {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
use once_cell::sync::Lazy;
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![glib::ParamSpecObject::builder::<Session>("session")
|
||||
.explicit_notify()
|
||||
.build()]
|
||||
});
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
match pspec.name() {
|
||||
"session" => self.obj().set_session(value.get().unwrap()),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
match pspec.name() {
|
||||
"session" => self.obj().session().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for SecurityPage {}
|
||||
|
||||
impl WidgetImpl for SecurityPage {}
|
||||
impl PreferencesPageImpl for SecurityPage {}
|
||||
|
||||
impl SecurityPage {
|
||||
/// Set the current session.
|
||||
fn set_session(&self, session: Option<Session>) {
|
||||
if self.session.upgrade() == session {
|
||||
return;
|
||||
}
|
||||
let obj = self.obj();
|
||||
|
||||
self.session.set(session.as_ref());
|
||||
obj.notify_session();
|
||||
|
||||
spawn!(clone!(@weak obj => async move {
|
||||
obj.load_cross_signing_status().await;
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
|
@ -88,25 +84,6 @@ impl SecurityPage {
|
|||
glib::Object::builder().property("session", session).build()
|
||||
}
|
||||
|
||||
/// The current session.
|
||||
pub fn session(&self) -> Option<Session> {
|
||||
self.imp().session.upgrade()
|
||||
}
|
||||
|
||||
/// Set the current session.
|
||||
pub fn set_session(&self, session: Option<Session>) {
|
||||
if self.session() == session {
|
||||
return;
|
||||
}
|
||||
|
||||
self.imp().session.set(session.as_ref());
|
||||
self.notify("session");
|
||||
|
||||
spawn!(clone!(@weak self as obj => async move {
|
||||
obj.load_cross_signing_status().await;
|
||||
}));
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
pub fn show_export_keys_page(&self) {
|
||||
let subpage = &*self.imp().import_export_keys_subpage;
|
||||
|
|
Loading…
Reference in New Issue