|
|
|
|
@ -14,7 +14,7 @@ use crate::{
|
|
|
|
|
prelude::*, |
|
|
|
|
session::model::{AddAltAliasError, RegisterLocalAliasError, Room}, |
|
|
|
|
spawn, toast, |
|
|
|
|
utils::DummyObject, |
|
|
|
|
utils::{DummyObject, SingleItemListModel}, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
mod imp { |
|
|
|
|
@ -34,32 +34,32 @@ mod imp {
|
|
|
|
|
#[properties(wrapper_type = super::AddressesSubpage)] |
|
|
|
|
pub struct AddressesSubpage { |
|
|
|
|
#[template_child] |
|
|
|
|
pub public_addresses_list: TemplateChild<gtk::ListBox>, |
|
|
|
|
public_addresses_list: TemplateChild<gtk::ListBox>, |
|
|
|
|
#[template_child] |
|
|
|
|
pub public_addresses_error_revealer: TemplateChild<gtk::Revealer>, |
|
|
|
|
public_addresses_error_revealer: TemplateChild<gtk::Revealer>, |
|
|
|
|
#[template_child] |
|
|
|
|
pub public_addresses_error: TemplateChild<gtk::Label>, |
|
|
|
|
public_addresses_error: TemplateChild<gtk::Label>, |
|
|
|
|
#[template_child] |
|
|
|
|
pub local_addresses_group: TemplateChild<adw::PreferencesGroup>, |
|
|
|
|
local_addresses_group: TemplateChild<adw::PreferencesGroup>, |
|
|
|
|
#[template_child] |
|
|
|
|
pub local_addresses_list: TemplateChild<gtk::ListBox>, |
|
|
|
|
local_addresses_list: TemplateChild<gtk::ListBox>, |
|
|
|
|
#[template_child] |
|
|
|
|
pub local_addresses_error_revealer: TemplateChild<gtk::Revealer>, |
|
|
|
|
local_addresses_error_revealer: TemplateChild<gtk::Revealer>, |
|
|
|
|
#[template_child] |
|
|
|
|
pub local_addresses_error: TemplateChild<gtk::Label>, |
|
|
|
|
local_addresses_error: TemplateChild<gtk::Label>, |
|
|
|
|
#[template_child] |
|
|
|
|
pub public_addresses_add_row: TemplateChild<EntryAddRow>, |
|
|
|
|
public_addresses_add_row: TemplateChild<EntryAddRow>, |
|
|
|
|
#[template_child] |
|
|
|
|
pub local_addresses_add_row: TemplateChild<SubstringEntryRow>, |
|
|
|
|
local_addresses_add_row: TemplateChild<SubstringEntryRow>, |
|
|
|
|
/// The room users will be invited to.
|
|
|
|
|
#[property(get, set = Self::set_room, construct_only)] |
|
|
|
|
pub room: glib::WeakRef<Room>, |
|
|
|
|
room: glib::WeakRef<Room>, |
|
|
|
|
/// The full list of public addresses.
|
|
|
|
|
pub public_addresses: OnceCell<gio::ListStore>, |
|
|
|
|
public_addresses: OnceCell<gio::ListStore>, |
|
|
|
|
/// The full list of local addresses.
|
|
|
|
|
pub local_addresses: gtk::StringList, |
|
|
|
|
local_addresses: gtk::StringList, |
|
|
|
|
aliases_changed_handler: RefCell<Option<glib::SignalHandlerId>>, |
|
|
|
|
pub public_addresses_completion: CompletionPopover, |
|
|
|
|
public_addresses_completion: CompletionPopover, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[glib::object_subclass] |
|
|
|
|
@ -70,7 +70,7 @@ mod imp {
|
|
|
|
|
|
|
|
|
|
fn class_init(klass: &mut Self::Class) { |
|
|
|
|
Self::bind_template(klass); |
|
|
|
|
Self::Type::bind_template_callbacks(klass); |
|
|
|
|
Self::bind_template_callbacks(klass); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn instance_init(obj: &InitializingObject<Self>) { |
|
|
|
|
@ -82,33 +82,31 @@ mod imp {
|
|
|
|
|
impl ObjectImpl for AddressesSubpage { |
|
|
|
|
fn constructed(&self) { |
|
|
|
|
self.parent_constructed(); |
|
|
|
|
let obj = self.obj(); |
|
|
|
|
|
|
|
|
|
let extra_items = gio::ListStore::new::<glib::Object>(); |
|
|
|
|
extra_items.append(&DummyObject::new("add")); |
|
|
|
|
let add_item = SingleItemListModel::new(&DummyObject::new("add")); |
|
|
|
|
|
|
|
|
|
// Public addresses.
|
|
|
|
|
let public_items = gio::ListStore::new::<glib::Object>(); |
|
|
|
|
public_items.append(self.public_addresses()); |
|
|
|
|
public_items.append(&extra_items); |
|
|
|
|
public_items.append(&add_item); |
|
|
|
|
|
|
|
|
|
let flattened_public_list = gtk::FlattenListModel::new(Some(public_items)); |
|
|
|
|
self.public_addresses_list.bind_model( |
|
|
|
|
Some(&flattened_public_list), |
|
|
|
|
clone!( |
|
|
|
|
#[weak] |
|
|
|
|
obj, |
|
|
|
|
#[weak(rename_to = imp)] |
|
|
|
|
self, |
|
|
|
|
#[upgrade_or_else] |
|
|
|
|
|| { adw::ActionRow::new().upcast() }, |
|
|
|
|
move |item| obj.create_public_address_row(item) |
|
|
|
|
move |item| imp.create_public_address_row(item) |
|
|
|
|
), |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
self.public_addresses_add_row.connect_changed(clone!( |
|
|
|
|
#[weak] |
|
|
|
|
obj, |
|
|
|
|
#[weak(rename_to = imp)] |
|
|
|
|
self, |
|
|
|
|
move |_| { |
|
|
|
|
obj.update_public_addresses_add_row(); |
|
|
|
|
imp.update_public_addresses_add_row(); |
|
|
|
|
} |
|
|
|
|
)); |
|
|
|
|
|
|
|
|
|
@ -163,25 +161,25 @@ mod imp {
|
|
|
|
|
// Local addresses.
|
|
|
|
|
let local_items = gio::ListStore::new::<glib::Object>(); |
|
|
|
|
local_items.append(&self.local_addresses); |
|
|
|
|
local_items.append(&extra_items); |
|
|
|
|
local_items.append(&add_item); |
|
|
|
|
|
|
|
|
|
let flattened_local_list = gtk::FlattenListModel::new(Some(local_items)); |
|
|
|
|
self.local_addresses_list.bind_model( |
|
|
|
|
Some(&flattened_local_list), |
|
|
|
|
clone!( |
|
|
|
|
#[weak] |
|
|
|
|
obj, |
|
|
|
|
#[weak(rename_to = imp)] |
|
|
|
|
self, |
|
|
|
|
#[upgrade_or_else] |
|
|
|
|
|| { adw::ActionRow::new().upcast() }, |
|
|
|
|
move |item| obj.create_local_address_row(item) |
|
|
|
|
move |item| imp.create_local_address_row(item) |
|
|
|
|
), |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
self.local_addresses_add_row.connect_changed(clone!( |
|
|
|
|
#[weak] |
|
|
|
|
obj, |
|
|
|
|
#[weak(rename_to = imp)] |
|
|
|
|
self, |
|
|
|
|
move |_| { |
|
|
|
|
obj.update_local_addresses_add_row(); |
|
|
|
|
imp.update_local_addresses_add_row(); |
|
|
|
|
} |
|
|
|
|
)); |
|
|
|
|
} |
|
|
|
|
@ -200,8 +198,9 @@ mod imp {
|
|
|
|
|
impl WidgetImpl for AddressesSubpage {} |
|
|
|
|
impl NavigationPageImpl for AddressesSubpage {} |
|
|
|
|
|
|
|
|
|
#[gtk::template_callbacks] |
|
|
|
|
impl AddressesSubpage { |
|
|
|
|
pub(super) fn public_addresses(&self) -> &gio::ListStore { |
|
|
|
|
fn public_addresses(&self) -> &gio::ListStore { |
|
|
|
|
self.public_addresses |
|
|
|
|
.get_or_init(gio::ListStore::new::<PublicAddress>) |
|
|
|
|
} |
|
|
|
|
@ -334,7 +333,7 @@ mod imp {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Update the list of local addresses.
|
|
|
|
|
pub(super) async fn update_local_addresses(&self) { |
|
|
|
|
async fn update_local_addresses(&self) { |
|
|
|
|
let Some(room) = self.room.upgrade() else { |
|
|
|
|
return; |
|
|
|
|
}; |
|
|
|
|
@ -377,26 +376,14 @@ mod imp {
|
|
|
|
|
.splice(self.local_addresses.n_items(), 0, &new_aliases); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
glib::wrapper! { |
|
|
|
|
/// Subpage to invite new members to a room.
|
|
|
|
|
pub struct AddressesSubpage(ObjectSubclass<imp::AddressesSubpage>) |
|
|
|
|
@extends gtk::Widget, gtk::Window, adw::NavigationPage, @implements gtk::Accessible; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[gtk::template_callbacks] |
|
|
|
|
impl AddressesSubpage { |
|
|
|
|
pub fn new(room: &Room) -> Self { |
|
|
|
|
glib::Object::builder().property("room", room).build() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Create a row for the given item in the public addresses section.
|
|
|
|
|
fn create_public_address_row(&self, item: &glib::Object) -> gtk::Widget { |
|
|
|
|
let imp = self.imp(); |
|
|
|
|
/// Create a row for the given item in the public addresses section.
|
|
|
|
|
fn create_public_address_row(&self, item: &glib::Object) -> gtk::Widget { |
|
|
|
|
let Some(address) = item.downcast_ref::<PublicAddress>() else { |
|
|
|
|
// It can only be the dummy item to add a new alias.
|
|
|
|
|
return self.public_addresses_add_row.clone().upcast(); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
if let Some(address) = item.downcast_ref::<PublicAddress>() { |
|
|
|
|
let alias = address.alias(); |
|
|
|
|
let row = RemovableRow::new(); |
|
|
|
|
row.set_title(alias.as_str()); |
|
|
|
|
@ -409,254 +396,246 @@ impl AddressesSubpage {
|
|
|
|
|
))); |
|
|
|
|
|
|
|
|
|
address.connect_is_main_notify(clone!( |
|
|
|
|
#[weak(rename_to = obj)] |
|
|
|
|
#[weak(rename_to = imp)] |
|
|
|
|
self, |
|
|
|
|
#[weak] |
|
|
|
|
row, |
|
|
|
|
move |address| { |
|
|
|
|
obj.update_public_row_is_main(&row, address.is_main()); |
|
|
|
|
imp.update_public_row_is_main(&row, address.is_main()); |
|
|
|
|
} |
|
|
|
|
)); |
|
|
|
|
self.update_public_row_is_main(&row, address.is_main()); |
|
|
|
|
|
|
|
|
|
row.connect_remove(clone!( |
|
|
|
|
#[weak(rename_to = obj)] |
|
|
|
|
#[weak(rename_to = imp)] |
|
|
|
|
self, |
|
|
|
|
move |row| { |
|
|
|
|
spawn!(clone!( |
|
|
|
|
#[weak] |
|
|
|
|
row, |
|
|
|
|
async move { |
|
|
|
|
obj.remove_public_address(&row).await; |
|
|
|
|
imp.remove_public_address(&row).await; |
|
|
|
|
} |
|
|
|
|
)); |
|
|
|
|
} |
|
|
|
|
)); |
|
|
|
|
|
|
|
|
|
row.upcast() |
|
|
|
|
} else { |
|
|
|
|
// It can only be the dummy item to add a new alias.
|
|
|
|
|
imp.public_addresses_add_row.clone().upcast() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Update the given row for whether the address it presents is the main
|
|
|
|
|
/// address or not.
|
|
|
|
|
fn update_public_row_is_main(&self, row: &RemovableRow, is_main: bool) { |
|
|
|
|
if is_main && !public_row_is_main(row) { |
|
|
|
|
let label = gtk::Label::builder() |
|
|
|
|
.label(gettext("Main Address")) |
|
|
|
|
.ellipsize(pango::EllipsizeMode::End) |
|
|
|
|
.build(); |
|
|
|
|
let image = gtk::Image::builder() |
|
|
|
|
.icon_name("checkmark-symbolic") |
|
|
|
|
.accessible_role(gtk::AccessibleRole::Presentation) |
|
|
|
|
.build(); |
|
|
|
|
let main_box = gtk::Box::builder() |
|
|
|
|
.spacing(6) |
|
|
|
|
.css_classes(["public-address-tag"]) |
|
|
|
|
.valign(gtk::Align::Center) |
|
|
|
|
.build(); |
|
|
|
|
|
|
|
|
|
main_box.append(&image); |
|
|
|
|
main_box.append(&label); |
|
|
|
|
|
|
|
|
|
row.update_relation(&[gtk::accessible::Relation::DescribedBy( |
|
|
|
|
&[label.upcast_ref()], |
|
|
|
|
)]); |
|
|
|
|
row.set_extra_suffix(Some(main_box)); |
|
|
|
|
} else if !is_main && !row.extra_suffix().is_some_and(|w| w.is::<LoadingButton>()) { |
|
|
|
|
let button = LoadingButton::new(); |
|
|
|
|
button.set_content_icon_name("checkmark-symbolic"); |
|
|
|
|
button.add_css_class("flat"); |
|
|
|
|
button.set_tooltip_text(Some(&gettext("Set as main address"))); |
|
|
|
|
button.set_valign(gtk::Align::Center); |
|
|
|
|
|
|
|
|
|
let accessible_label = gettext_f( |
|
|
|
|
// Translators: Do NOT translate the content between '{' and '}',
|
|
|
|
|
// this is a variable name.
|
|
|
|
|
"Set “{address}” as main address", |
|
|
|
|
&[("address", &row.title())], |
|
|
|
|
); |
|
|
|
|
button.update_property(&[gtk::accessible::Property::Label(&accessible_label)]); |
|
|
|
|
|
|
|
|
|
button.connect_clicked(clone!( |
|
|
|
|
#[weak(rename_to = obj)] |
|
|
|
|
self, |
|
|
|
|
#[weak] |
|
|
|
|
row, |
|
|
|
|
move |_| { |
|
|
|
|
spawn!(async move { |
|
|
|
|
obj.set_main_public_address(&row).await; |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
)); |
|
|
|
|
/// Update the given row for whether the address it presents is the main
|
|
|
|
|
/// address or not.
|
|
|
|
|
fn update_public_row_is_main(&self, row: &RemovableRow, is_main: bool) { |
|
|
|
|
if is_main && !public_row_is_main(row) { |
|
|
|
|
let label = gtk::Label::builder() |
|
|
|
|
.label(gettext("Main Address")) |
|
|
|
|
.ellipsize(pango::EllipsizeMode::End) |
|
|
|
|
.build(); |
|
|
|
|
let image = gtk::Image::builder() |
|
|
|
|
.icon_name("checkmark-symbolic") |
|
|
|
|
.accessible_role(gtk::AccessibleRole::Presentation) |
|
|
|
|
.build(); |
|
|
|
|
let main_box = gtk::Box::builder() |
|
|
|
|
.spacing(6) |
|
|
|
|
.css_classes(["public-address-tag"]) |
|
|
|
|
.valign(gtk::Align::Center) |
|
|
|
|
.build(); |
|
|
|
|
|
|
|
|
|
main_box.append(&image); |
|
|
|
|
main_box.append(&label); |
|
|
|
|
|
|
|
|
|
row.update_relation(&[gtk::accessible::Relation::DescribedBy(&[ |
|
|
|
|
label.upcast_ref() |
|
|
|
|
])]); |
|
|
|
|
row.set_extra_suffix(Some(main_box)); |
|
|
|
|
} else if !is_main && !row.extra_suffix().is_some_and(|w| w.is::<LoadingButton>()) { |
|
|
|
|
let button = LoadingButton::new(); |
|
|
|
|
button.set_content_icon_name("checkmark-symbolic"); |
|
|
|
|
button.add_css_class("flat"); |
|
|
|
|
button.set_tooltip_text(Some(&gettext("Set as main address"))); |
|
|
|
|
button.set_valign(gtk::Align::Center); |
|
|
|
|
|
|
|
|
|
let accessible_label = gettext_f( |
|
|
|
|
// Translators: Do NOT translate the content between '{' and '}',
|
|
|
|
|
// this is a variable name.
|
|
|
|
|
"Set “{address}” as main address", |
|
|
|
|
&[("address", &row.title())], |
|
|
|
|
); |
|
|
|
|
button.update_property(&[gtk::accessible::Property::Label(&accessible_label)]); |
|
|
|
|
|
|
|
|
|
button.connect_clicked(clone!( |
|
|
|
|
#[weak(rename_to = imp)] |
|
|
|
|
self, |
|
|
|
|
#[weak] |
|
|
|
|
row, |
|
|
|
|
move |_| { |
|
|
|
|
spawn!(async move { |
|
|
|
|
imp.set_main_public_address(&row).await; |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
)); |
|
|
|
|
|
|
|
|
|
row.set_extra_suffix(Some(button)); |
|
|
|
|
row.set_extra_suffix(Some(button)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Remove the public address from the given row.
|
|
|
|
|
async fn remove_public_address(&self, row: &RemovableRow) { |
|
|
|
|
let Some(room) = self.room() else { |
|
|
|
|
return; |
|
|
|
|
}; |
|
|
|
|
let Ok(alias) = RoomAliasId::parse(row.title()) else { |
|
|
|
|
error!("Cannot remove address with invalid alias"); |
|
|
|
|
return; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
let imp = self.imp(); |
|
|
|
|
let aliases = room.aliases(); |
|
|
|
|
|
|
|
|
|
imp.public_addresses_list.set_sensitive(false); |
|
|
|
|
row.set_is_loading(true); |
|
|
|
|
|
|
|
|
|
let result = if public_row_is_main(row) { |
|
|
|
|
aliases.remove_canonical_alias(&alias).await |
|
|
|
|
} else { |
|
|
|
|
aliases.remove_alt_alias(&alias).await |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
if result.is_err() { |
|
|
|
|
toast!(self, gettext("Could not remove public address")); |
|
|
|
|
imp.public_addresses_list.set_sensitive(true); |
|
|
|
|
row.set_is_loading(false); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
/// Remove the public address from the given row.
|
|
|
|
|
async fn remove_public_address(&self, row: &RemovableRow) { |
|
|
|
|
let Some(room) = self.room.upgrade() else { |
|
|
|
|
return; |
|
|
|
|
}; |
|
|
|
|
let Ok(alias) = RoomAliasId::parse(row.title()) else { |
|
|
|
|
error!("Cannot remove address with invalid alias"); |
|
|
|
|
return; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
let aliases = room.aliases(); |
|
|
|
|
|
|
|
|
|
self.public_addresses_list.set_sensitive(false); |
|
|
|
|
row.set_is_loading(true); |
|
|
|
|
|
|
|
|
|
let result = if public_row_is_main(row) { |
|
|
|
|
aliases.remove_canonical_alias(&alias).await |
|
|
|
|
} else { |
|
|
|
|
aliases.remove_alt_alias(&alias).await |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
/// Set the address from the given row as the main public address.
|
|
|
|
|
async fn set_main_public_address(&self, row: &RemovableRow) { |
|
|
|
|
let Some(room) = self.room() else { |
|
|
|
|
return; |
|
|
|
|
}; |
|
|
|
|
let Some(button) = row.extra_suffix().and_downcast::<LoadingButton>() else { |
|
|
|
|
return; |
|
|
|
|
}; |
|
|
|
|
let Ok(alias) = RoomAliasId::parse(row.title()) else { |
|
|
|
|
error!("Cannot set main public address with invalid alias"); |
|
|
|
|
return; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
let imp = self.imp(); |
|
|
|
|
let aliases = room.aliases(); |
|
|
|
|
|
|
|
|
|
imp.public_addresses_list.set_sensitive(false); |
|
|
|
|
button.set_is_loading(true); |
|
|
|
|
|
|
|
|
|
if aliases.set_canonical_alias(alias).await.is_err() { |
|
|
|
|
toast!(self, gettext("Could not set main public address")); |
|
|
|
|
imp.public_addresses_list.set_sensitive(true); |
|
|
|
|
button.set_is_loading(false); |
|
|
|
|
if result.is_err() { |
|
|
|
|
let obj = self.obj(); |
|
|
|
|
toast!(obj, gettext("Could not remove public address")); |
|
|
|
|
self.public_addresses_list.set_sensitive(true); |
|
|
|
|
row.set_is_loading(false); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Update the public addresses add row for the current state.
|
|
|
|
|
fn update_public_addresses_add_row(&self) { |
|
|
|
|
self.imp() |
|
|
|
|
.public_addresses_add_row |
|
|
|
|
.set_inhibit_add(!self.can_add_public_address()); |
|
|
|
|
} |
|
|
|
|
/// Set the address from the given row as the main public address.
|
|
|
|
|
async fn set_main_public_address(&self, row: &RemovableRow) { |
|
|
|
|
let Some(room) = self.room.upgrade() else { |
|
|
|
|
return; |
|
|
|
|
}; |
|
|
|
|
let Some(button) = row.extra_suffix().and_downcast::<LoadingButton>() else { |
|
|
|
|
return; |
|
|
|
|
}; |
|
|
|
|
let Ok(alias) = RoomAliasId::parse(row.title()) else { |
|
|
|
|
error!("Cannot set main public address with invalid alias"); |
|
|
|
|
return; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
let aliases = room.aliases(); |
|
|
|
|
|
|
|
|
|
self.public_addresses_list.set_sensitive(false); |
|
|
|
|
button.set_is_loading(true); |
|
|
|
|
|
|
|
|
|
/// Activate the auto-completion of the public addresses add row.
|
|
|
|
|
#[template_callback] |
|
|
|
|
async fn handle_public_addresses_add_row_activated(&self) { |
|
|
|
|
if !self |
|
|
|
|
.imp() |
|
|
|
|
.public_addresses_completion |
|
|
|
|
.activate_selected_row() |
|
|
|
|
{ |
|
|
|
|
self.add_public_address().await; |
|
|
|
|
if aliases.set_canonical_alias(alias).await.is_err() { |
|
|
|
|
let obj = self.obj(); |
|
|
|
|
toast!(obj, gettext("Could not set main public address")); |
|
|
|
|
self.public_addresses_list.set_sensitive(true); |
|
|
|
|
button.set_is_loading(false); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Add a an address to the public list.
|
|
|
|
|
#[template_callback] |
|
|
|
|
async fn add_public_address(&self) { |
|
|
|
|
if !self.can_add_public_address() { |
|
|
|
|
return; |
|
|
|
|
/// Update the public addresses add row for the current state.
|
|
|
|
|
fn update_public_addresses_add_row(&self) { |
|
|
|
|
self.public_addresses_add_row |
|
|
|
|
.set_inhibit_add(!self.can_add_public_address()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let Some(room) = self.room() else { |
|
|
|
|
return; |
|
|
|
|
}; |
|
|
|
|
/// Activate the auto-completion of the public addresses add row.
|
|
|
|
|
#[template_callback] |
|
|
|
|
async fn handle_public_addresses_add_row_activated(&self) { |
|
|
|
|
if !self.public_addresses_completion.activate_selected_row() { |
|
|
|
|
self.add_public_address().await; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let imp = self.imp(); |
|
|
|
|
let row = &imp.public_addresses_add_row; |
|
|
|
|
/// Add a an address to the public list.
|
|
|
|
|
#[template_callback] |
|
|
|
|
async fn add_public_address(&self) { |
|
|
|
|
if !self.can_add_public_address() { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let Ok(alias) = RoomAliasId::parse(row.text()) else { |
|
|
|
|
error!("Cannot add public address with invalid alias"); |
|
|
|
|
return; |
|
|
|
|
}; |
|
|
|
|
let Some(room) = self.room.upgrade() else { |
|
|
|
|
return; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
imp.public_addresses_list.set_sensitive(false); |
|
|
|
|
row.set_is_loading(true); |
|
|
|
|
imp.public_addresses_error_revealer.set_reveal_child(false); |
|
|
|
|
let row = &self.public_addresses_add_row; |
|
|
|
|
|
|
|
|
|
let aliases = room.aliases(); |
|
|
|
|
match aliases.add_alt_alias(alias).await { |
|
|
|
|
Ok(()) => { |
|
|
|
|
row.set_text(""); |
|
|
|
|
} |
|
|
|
|
Err(error) => { |
|
|
|
|
toast!(self, gettext("Could not add public address")); |
|
|
|
|
let Ok(alias) = RoomAliasId::parse(row.text()) else { |
|
|
|
|
error!("Cannot add public address with invalid alias"); |
|
|
|
|
return; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
let label = match error { |
|
|
|
|
AddAltAliasError::NotRegistered => { |
|
|
|
|
Some(gettext("This address is not registered as a local address")) |
|
|
|
|
} |
|
|
|
|
AddAltAliasError::InvalidRoomId => { |
|
|
|
|
Some(gettext("This address does not belong to this room")) |
|
|
|
|
} |
|
|
|
|
AddAltAliasError::Other => None, |
|
|
|
|
}; |
|
|
|
|
self.public_addresses_list.set_sensitive(false); |
|
|
|
|
row.set_is_loading(true); |
|
|
|
|
self.public_addresses_error_revealer.set_reveal_child(false); |
|
|
|
|
|
|
|
|
|
if let Some(label) = label { |
|
|
|
|
imp.public_addresses_error.set_label(&label); |
|
|
|
|
imp.public_addresses_error_revealer.set_reveal_child(true); |
|
|
|
|
let aliases = room.aliases(); |
|
|
|
|
match aliases.add_alt_alias(alias).await { |
|
|
|
|
Ok(()) => { |
|
|
|
|
row.set_text(""); |
|
|
|
|
} |
|
|
|
|
Err(error) => { |
|
|
|
|
let obj = self.obj(); |
|
|
|
|
toast!(obj, gettext("Could not add public address")); |
|
|
|
|
|
|
|
|
|
imp.public_addresses_list.set_sensitive(true); |
|
|
|
|
row.set_is_loading(false); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
let label = match error { |
|
|
|
|
AddAltAliasError::NotRegistered => { |
|
|
|
|
Some(gettext("This address is not registered as a local address")) |
|
|
|
|
} |
|
|
|
|
AddAltAliasError::InvalidRoomId => { |
|
|
|
|
Some(gettext("This address does not belong to this room")) |
|
|
|
|
} |
|
|
|
|
AddAltAliasError::Other => None, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
/// Whether the user can add the current address to the public list.
|
|
|
|
|
fn can_add_public_address(&self) -> bool { |
|
|
|
|
let imp = self.imp(); |
|
|
|
|
let new_address = imp.public_addresses_add_row.text(); |
|
|
|
|
if let Some(label) = label { |
|
|
|
|
self.public_addresses_error.set_label(&label); |
|
|
|
|
self.public_addresses_error_revealer.set_reveal_child(true); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Cannot add an empty address.
|
|
|
|
|
if new_address.is_empty() { |
|
|
|
|
return false; |
|
|
|
|
self.public_addresses_list.set_sensitive(true); |
|
|
|
|
row.set_is_loading(false); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Cannot add an invalid alias.
|
|
|
|
|
let Ok(new_alias) = RoomAliasId::parse(new_address) else { |
|
|
|
|
return false; |
|
|
|
|
}; |
|
|
|
|
/// Whether the user can add the current address to the public list.
|
|
|
|
|
fn can_add_public_address(&self) -> bool { |
|
|
|
|
let new_address = self.public_addresses_add_row.text(); |
|
|
|
|
|
|
|
|
|
// Cannot add a duplicate address.
|
|
|
|
|
for public_address in imp.public_addresses().iter::<PublicAddress>() { |
|
|
|
|
let Ok(public_address) = public_address else { |
|
|
|
|
// The iterator is broken.
|
|
|
|
|
// Cannot add an empty address.
|
|
|
|
|
if new_address.is_empty() { |
|
|
|
|
return false; |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if *public_address.alias() == new_alias { |
|
|
|
|
// Cannot add an invalid alias.
|
|
|
|
|
let Ok(new_alias) = RoomAliasId::parse(new_address) else { |
|
|
|
|
return false; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// Cannot add a duplicate address.
|
|
|
|
|
for public_address in self.public_addresses().iter::<PublicAddress>() { |
|
|
|
|
let Ok(public_address) = public_address else { |
|
|
|
|
// The iterator is broken.
|
|
|
|
|
return false; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
if *public_address.alias() == new_alias { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
true |
|
|
|
|
} |
|
|
|
|
true |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Create a row for the given item in the public addresses section.
|
|
|
|
|
fn create_local_address_row(&self, item: &glib::Object) -> gtk::Widget { |
|
|
|
|
let imp = self.imp(); |
|
|
|
|
/// Create a row for the given item in the public addresses section.
|
|
|
|
|
fn create_local_address_row(&self, item: &glib::Object) -> gtk::Widget { |
|
|
|
|
let Some(string_obj) = item.downcast_ref::<gtk::StringObject>() else { |
|
|
|
|
// It can only be the dummy item to add a new alias.
|
|
|
|
|
return self.local_addresses_add_row.clone().upcast(); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
if let Some(string_obj) = item.downcast_ref::<gtk::StringObject>() { |
|
|
|
|
let alias = string_obj.string(); |
|
|
|
|
let row = RemovableRow::new(); |
|
|
|
|
row.set_title(&alias); |
|
|
|
|
@ -669,153 +648,163 @@ impl AddressesSubpage {
|
|
|
|
|
))); |
|
|
|
|
|
|
|
|
|
row.connect_remove(clone!( |
|
|
|
|
#[weak(rename_to = obj)] |
|
|
|
|
#[weak(rename_to = imp)] |
|
|
|
|
self, |
|
|
|
|
move |row| { |
|
|
|
|
spawn!(clone!( |
|
|
|
|
#[weak] |
|
|
|
|
row, |
|
|
|
|
async move { |
|
|
|
|
obj.unregister_local_address(&row).await; |
|
|
|
|
imp.unregister_local_address(&row).await; |
|
|
|
|
} |
|
|
|
|
)); |
|
|
|
|
} |
|
|
|
|
)); |
|
|
|
|
|
|
|
|
|
row.upcast() |
|
|
|
|
} else { |
|
|
|
|
imp.local_addresses_add_row.clone().upcast() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Unregister the local address from the given row.
|
|
|
|
|
async fn unregister_local_address(&self, row: &RemovableRow) { |
|
|
|
|
let Some(room) = self.room() else { |
|
|
|
|
return; |
|
|
|
|
}; |
|
|
|
|
let Ok(alias) = RoomAliasId::parse(row.title()) else { |
|
|
|
|
error!("Cannot unregister local address with invalid alias"); |
|
|
|
|
return; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
let aliases = room.aliases(); |
|
|
|
|
|
|
|
|
|
row.set_is_loading(true); |
|
|
|
|
/// Unregister the local address from the given row.
|
|
|
|
|
async fn unregister_local_address(&self, row: &RemovableRow) { |
|
|
|
|
let Some(room) = self.room.upgrade() else { |
|
|
|
|
return; |
|
|
|
|
}; |
|
|
|
|
let Ok(alias) = RoomAliasId::parse(row.title()) else { |
|
|
|
|
error!("Cannot unregister local address with invalid alias"); |
|
|
|
|
return; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
if aliases.unregister_local_alias(alias).await.is_err() { |
|
|
|
|
toast!(self, gettext("Could not unregister local address")); |
|
|
|
|
} |
|
|
|
|
let aliases = room.aliases(); |
|
|
|
|
|
|
|
|
|
self.imp().update_local_addresses().await; |
|
|
|
|
row.set_is_loading(true); |
|
|
|
|
|
|
|
|
|
row.set_is_loading(false); |
|
|
|
|
} |
|
|
|
|
if aliases.unregister_local_alias(alias).await.is_err() { |
|
|
|
|
let obj = self.obj(); |
|
|
|
|
toast!(obj, gettext("Could not unregister local address")); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// The full new address in the public addresses add row.
|
|
|
|
|
///
|
|
|
|
|
/// Returns `None` if the localpart is empty.
|
|
|
|
|
fn new_local_address(&self) -> Option<String> { |
|
|
|
|
let row = &self.imp().local_addresses_add_row; |
|
|
|
|
let localpart = row.text(); |
|
|
|
|
self.update_local_addresses().await; |
|
|
|
|
|
|
|
|
|
if localpart.is_empty() { |
|
|
|
|
return None; |
|
|
|
|
row.set_is_loading(false); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let server_name = row.suffix_text(); |
|
|
|
|
Some(format!("#{localpart}{server_name}")) |
|
|
|
|
} |
|
|
|
|
/// The full new address in the public addresses add row.
|
|
|
|
|
///
|
|
|
|
|
/// Returns `None` if the localpart is empty.
|
|
|
|
|
fn new_local_address(&self) -> Option<String> { |
|
|
|
|
let row = &self.local_addresses_add_row; |
|
|
|
|
let localpart = row.text(); |
|
|
|
|
|
|
|
|
|
/// Update the public addresses add row for the current state.
|
|
|
|
|
fn update_local_addresses_add_row(&self) { |
|
|
|
|
let row = &self.imp().local_addresses_add_row; |
|
|
|
|
|
|
|
|
|
row.set_inhibit_add(!self.can_register_local_address()); |
|
|
|
|
if localpart.is_empty() { |
|
|
|
|
return None; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let accessible_label = self.new_local_address().map(|address| { |
|
|
|
|
gettext_f( |
|
|
|
|
// Translators: Do NOT translate the content between '{' and '}',
|
|
|
|
|
// this is a variable name.
|
|
|
|
|
"Register “{address}”", |
|
|
|
|
&[("address", &address)], |
|
|
|
|
) |
|
|
|
|
}); |
|
|
|
|
row.set_add_button_accessible_label(accessible_label); |
|
|
|
|
} |
|
|
|
|
let server_name = row.suffix_text(); |
|
|
|
|
Some(format!("#{localpart}{server_name}")) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Register a local address.
|
|
|
|
|
#[template_callback] |
|
|
|
|
async fn register_local_address(&self) { |
|
|
|
|
if !self.can_register_local_address() { |
|
|
|
|
return; |
|
|
|
|
/// Update the public addresses add row for the current state.
|
|
|
|
|
fn update_local_addresses_add_row(&self) { |
|
|
|
|
let row = &self.local_addresses_add_row; |
|
|
|
|
|
|
|
|
|
row.set_inhibit_add(!self.can_register_local_address()); |
|
|
|
|
|
|
|
|
|
let accessible_label = self.new_local_address().map(|address| { |
|
|
|
|
gettext_f( |
|
|
|
|
// Translators: Do NOT translate the content between '{' and '}',
|
|
|
|
|
// this is a variable name.
|
|
|
|
|
"Register “{address}”", |
|
|
|
|
&[("address", &address)], |
|
|
|
|
) |
|
|
|
|
}); |
|
|
|
|
row.set_add_button_accessible_label(accessible_label); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let Some(room) = self.room() else { |
|
|
|
|
return; |
|
|
|
|
}; |
|
|
|
|
/// Register a local address.
|
|
|
|
|
#[template_callback] |
|
|
|
|
async fn register_local_address(&self) { |
|
|
|
|
if !self.can_register_local_address() { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let Some(new_address) = self.new_local_address() else { |
|
|
|
|
return; |
|
|
|
|
}; |
|
|
|
|
let Ok(alias) = RoomAliasId::parse(new_address) else { |
|
|
|
|
error!("Cannot register local address with invalid alias"); |
|
|
|
|
return; |
|
|
|
|
}; |
|
|
|
|
let Some(room) = self.room.upgrade() else { |
|
|
|
|
return; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
let imp = self.imp(); |
|
|
|
|
let row = &imp.local_addresses_add_row; |
|
|
|
|
row.set_is_loading(true); |
|
|
|
|
imp.local_addresses_error_revealer.set_reveal_child(false); |
|
|
|
|
let Some(new_address) = self.new_local_address() else { |
|
|
|
|
return; |
|
|
|
|
}; |
|
|
|
|
let Ok(alias) = RoomAliasId::parse(new_address) else { |
|
|
|
|
error!("Cannot register local address with invalid alias"); |
|
|
|
|
return; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
let aliases = room.aliases(); |
|
|
|
|
let row = &self.local_addresses_add_row; |
|
|
|
|
row.set_is_loading(true); |
|
|
|
|
self.local_addresses_error_revealer.set_reveal_child(false); |
|
|
|
|
|
|
|
|
|
match aliases.register_local_alias(alias).await { |
|
|
|
|
Ok(()) => { |
|
|
|
|
row.set_text(""); |
|
|
|
|
} |
|
|
|
|
Err(error) => { |
|
|
|
|
toast!(self, gettext("Could not register local address")); |
|
|
|
|
let aliases = room.aliases(); |
|
|
|
|
|
|
|
|
|
if let RegisterLocalAliasError::AlreadyInUse = error { |
|
|
|
|
imp.local_addresses_error |
|
|
|
|
.set_label(&gettext("This address is already registered")); |
|
|
|
|
imp.local_addresses_error_revealer.set_reveal_child(true); |
|
|
|
|
match aliases.register_local_alias(alias).await { |
|
|
|
|
Ok(()) => { |
|
|
|
|
row.set_text(""); |
|
|
|
|
} |
|
|
|
|
Err(error) => { |
|
|
|
|
let obj = self.obj(); |
|
|
|
|
toast!(obj, gettext("Could not register local address")); |
|
|
|
|
|
|
|
|
|
if let RegisterLocalAliasError::AlreadyInUse = error { |
|
|
|
|
self.local_addresses_error |
|
|
|
|
.set_label(&gettext("This address is already registered")); |
|
|
|
|
self.local_addresses_error_revealer.set_reveal_child(true); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
imp.update_local_addresses().await; |
|
|
|
|
self.update_local_addresses().await; |
|
|
|
|
|
|
|
|
|
row.set_is_loading(false); |
|
|
|
|
} |
|
|
|
|
row.set_is_loading(false); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Whether the user can add the current address to the local list.
|
|
|
|
|
fn can_register_local_address(&self) -> bool { |
|
|
|
|
let imp = self.imp(); |
|
|
|
|
|
|
|
|
|
// Cannot add an empty address.
|
|
|
|
|
let Some(new_address) = self.new_local_address() else { |
|
|
|
|
return false; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// Cannot add an invalid alias.
|
|
|
|
|
let Ok(new_alias) = RoomAliasId::parse(new_address) else { |
|
|
|
|
return false; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// Cannot add a duplicate address.
|
|
|
|
|
for local_address in imp.public_addresses().iter::<glib::Object>() { |
|
|
|
|
let Some(local_address) = local_address.ok().and_downcast::<gtk::StringObject>() else { |
|
|
|
|
// The iterator is broken.
|
|
|
|
|
return true; |
|
|
|
|
/// Whether the user can add the current address to the local list.
|
|
|
|
|
fn can_register_local_address(&self) -> bool { |
|
|
|
|
// Cannot add an empty address.
|
|
|
|
|
let Some(new_address) = self.new_local_address() else { |
|
|
|
|
return false; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
if local_address.string() == new_alias.as_str() { |
|
|
|
|
// Cannot add an invalid alias.
|
|
|
|
|
let Ok(new_alias) = RoomAliasId::parse(new_address) else { |
|
|
|
|
return false; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// Cannot add a duplicate address.
|
|
|
|
|
for local_address in self.public_addresses().iter::<glib::Object>() { |
|
|
|
|
let Some(local_address) = local_address.ok().and_downcast::<gtk::StringObject>() |
|
|
|
|
else { |
|
|
|
|
// The iterator is broken.
|
|
|
|
|
return true; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
if local_address.string() == new_alias.as_str() { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
true |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
true |
|
|
|
|
glib::wrapper! { |
|
|
|
|
/// Subpage to manage the public addresses of a room.
|
|
|
|
|
pub struct AddressesSubpage(ObjectSubclass<imp::AddressesSubpage>) |
|
|
|
|
@extends gtk::Widget, gtk::Window, adw::NavigationPage, @implements gtk::Accessible; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl AddressesSubpage { |
|
|
|
|
pub fn new(room: &Room) -> Self { |
|
|
|
|
glib::Object::builder().property("room", room).build() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|