fractal/components/dialogs/
join_room.rsuse adw::{prelude::*, subclass::prelude::*};
use gettextrs::gettext;
use gtk::{glib, glib::clone, CompositeTemplate};
use super::ToastableDialog;
use crate::{
components::{Avatar, LoadingButton},
i18n::ngettext_f,
prelude::*,
session::model::{RemoteRoom, Session},
toast,
utils::{
matrix::{MatrixIdUri, MatrixRoomIdUri},
LoadingState,
},
Window,
};
mod imp {
use std::cell::{Cell, RefCell};
use glib::subclass::InitializingObject;
use super::*;
#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
#[template(resource = "/org/gnome/Fractal/ui/components/dialogs/join_room.ui")]
#[properties(wrapper_type = super::JoinRoomDialog)]
pub struct JoinRoomDialog {
#[template_child]
go_back_btn: TemplateChild<gtk::Button>,
#[template_child]
stack: TemplateChild<gtk::Stack>,
#[template_child]
entry_page: TemplateChild<gtk::Box>,
#[template_child]
search_entry: TemplateChild<gtk::SearchEntry>,
#[template_child]
look_up_btn: TemplateChild<LoadingButton>,
#[template_child]
room_avatar: TemplateChild<Avatar>,
#[template_child]
room_name: TemplateChild<gtk::Label>,
#[template_child]
room_alias: TemplateChild<gtk::Label>,
#[template_child]
room_topic: TemplateChild<gtk::Label>,
#[template_child]
room_members_box: TemplateChild<gtk::Box>,
#[template_child]
room_members_count: TemplateChild<gtk::Label>,
#[template_child]
join_btn: TemplateChild<LoadingButton>,
#[property(get, set = Self::set_session, construct_only)]
session: glib::WeakRef<Session>,
uri: RefCell<Option<MatrixRoomIdUri>>,
#[property(get)]
room: RefCell<Option<RemoteRoom>>,
disable_go_back: Cell<bool>,
}
#[glib::object_subclass]
impl ObjectSubclass for JoinRoomDialog {
const NAME: &'static str = "JoinRoomDialog";
type Type = super::JoinRoomDialog;
type ParentType = ToastableDialog;
fn class_init(klass: &mut Self::Class) {
Self::bind_template(klass);
Self::bind_template_callbacks(klass);
}
fn instance_init(obj: &InitializingObject<Self>) {
obj.init_template();
}
}
#[glib::derived_properties]
impl ObjectImpl for JoinRoomDialog {
fn constructed(&self) {
self.parent_constructed();
let obj = self.obj();
self.room_topic.connect_activate_link(clone!(
#[weak]
obj,
#[upgrade_or]
glib::Propagation::Proceed,
move |_, uri| {
let Ok(uri) = MatrixIdUri::parse(uri) else {
return glib::Propagation::Proceed;
};
let Some(parent_window) =
obj.ancestor(Window::static_type()).and_downcast::<Window>()
else {
return glib::Propagation::Proceed;
};
parent_window.session_view().show_matrix_uri(uri);
glib::Propagation::Stop
}
));
}
}
impl WidgetImpl for JoinRoomDialog {}
impl AdwDialogImpl for JoinRoomDialog {}
impl ToastableDialogImpl for JoinRoomDialog {}
#[gtk::template_callbacks]
impl JoinRoomDialog {
fn set_session(&self, session: Option<&Session>) {
self.session.set(session);
self.obj().notify_session();
self.update_entry_page();
}
pub(super) fn set_uri(&self, uri: MatrixRoomIdUri) {
self.uri.replace(Some(uri.clone()));
self.disable_go_back(true);
self.set_visible_page("loading");
self.look_up_room_inner(uri);
}
pub(super) fn set_room(&self, room: Option<RemoteRoom>) {
if *self.room.borrow() == room {
return;
}
self.room.replace(room.clone());
if let Some(room) = room {
if matches!(
room.loading_state(),
LoadingState::Ready | LoadingState::Error
) {
self.fill_details();
} else {
room.connect_loading_state_notify(clone!(
#[weak(rename_to = imp)]
self,
move |room| {
if matches!(
room.loading_state(),
LoadingState::Ready | LoadingState::Error
) {
imp.fill_details();
}
}
));
}
}
self.obj().notify_room();
}
pub(super) fn disable_go_back(&self, disable: bool) {
self.disable_go_back.set(disable);
}
fn can_go_back(&self) -> bool {
!self.disable_go_back.get()
&& self.stack.visible_child_name().as_deref() == Some("details")
}
fn set_visible_page(&self, page_name: &str) {
self.stack.set_visible_child_name(page_name);
self.go_back_btn.set_visible(self.can_go_back());
}
#[template_callback]
fn update_entry_page(&self) {
let Some(session) = self.session.upgrade() else {
self.entry_page.set_sensitive(false);
return;
};
self.entry_page.set_sensitive(true);
let Some(uri) = MatrixRoomIdUri::parse(&self.search_entry.text()) else {
self.look_up_btn.set_sensitive(false);
self.uri.take();
return;
};
self.look_up_btn.set_sensitive(true);
let id = uri.id.clone();
self.uri.replace(Some(uri));
if session.room_list().joined_room(&id).is_some() {
self.look_up_btn.set_content_label(gettext("View"));
} else {
self.look_up_btn.set_content_label(gettext("Look Up"));
}
}
#[template_callback]
fn look_up_room(&self) {
let Some(uri) = self.uri.borrow().clone() else {
return;
};
let obj = self.obj();
let Some(window) = obj.root().and_downcast::<Window>() else {
return;
};
self.look_up_btn.set_is_loading(true);
self.entry_page.set_sensitive(false);
if window.session_view().select_room_if_exists(&uri.id) {
obj.close();
} else {
self.look_up_room_inner(uri);
}
}
fn look_up_room_inner(&self, uri: MatrixRoomIdUri) {
let Some(session) = self.session.upgrade() else {
return;
};
self.go_back_btn.set_sensitive(true);
self.join_btn.set_is_loading(false);
let room = RemoteRoom::new(&session, uri);
self.set_room(Some(room));
}
fn fill_details(&self) {
let Some(room) = self.room.borrow().clone() else {
return;
};
self.room_name.set_label(&room.display_name());
let alias = room.alias();
if let Some(alias) = &alias {
self.room_alias.set_label(alias.as_str());
}
self.room_alias
.set_visible(room.name().is_some() && alias.is_some());
self.room_avatar.set_data(Some(room.avatar_data()));
if room.loading_state() == LoadingState::Error {
self.room_topic.set_label(&gettext(
"The room details cannot be previewed. It can be because the room is not known by the homeserver or because its details are private. You can still try to join it."
));
self.room_topic.set_visible(true);
self.room_members_box.set_visible(false);
self.set_visible_page("details");
return;
}
if let Some(topic) = room.topic_linkified() {
self.room_topic.set_label(&topic);
self.room_topic.set_visible(true);
} else {
self.room_topic.set_visible(false);
}
let members_count = room.joined_members_count();
self.room_members_count
.set_label(&members_count.to_string());
let members_tooltip = ngettext_f(
"1 member",
"{n} members",
members_count,
&[("n", &members_count.to_string())],
);
self.room_members_box
.set_tooltip_text(Some(&members_tooltip));
self.room_members_box.set_visible(true);
self.set_visible_page("details");
}
#[template_callback]
async fn join_room(&self) {
let Some(session) = self.session.upgrade() else {
return;
};
let Some(room) = self.room.borrow().clone() else {
return;
};
self.go_back_btn.set_sensitive(false);
self.join_btn.set_is_loading(true);
let room_list = session.room_list();
let uri = room.uri().clone();
match room_list.join_by_id_or_alias(uri.id, uri.via).await {
Ok(room_id) => {
let obj = self.obj();
if let Some(room) = room_list.get_wait(&room_id).await {
if let Some(window) = obj.root().and_downcast_ref::<Window>() {
window.session_view().select_room(Some(room));
}
}
obj.close();
}
Err(error) => {
let obj = self.obj();
toast!(obj, error);
self.join_btn.set_is_loading(false);
self.go_back_btn.set_sensitive(true);
}
}
}
#[template_callback]
fn go_back(&self) {
if self.can_go_back() {
self.look_up_btn.set_is_loading(false);
self.entry_page.set_sensitive(true);
self.set_visible_page("entry");
} else {
self.obj().close();
}
}
}
}
glib::wrapper! {
pub struct JoinRoomDialog(ObjectSubclass<imp::JoinRoomDialog>)
@extends gtk::Widget, adw::Dialog, ToastableDialog, @implements gtk::Accessible;
}
#[gtk::template_callbacks]
impl JoinRoomDialog {
pub fn new(session: &Session) -> Self {
glib::Object::builder().property("session", session).build()
}
pub(crate) fn set_uri(&self, uri: MatrixRoomIdUri) {
self.imp().set_uri(uri);
}
pub(crate) fn set_room(&self, room: RemoteRoom) {
let imp = self.imp();
imp.disable_go_back(true);
imp.set_room(Some(room));
}
}