diff --git a/data/resources/ui/join-room-dialog.ui b/data/resources/ui/join-room-dialog.ui index d12ba2e7..f3c6af79 100644 --- a/data/resources/ui/join-room-dialog.ui +++ b/data/resources/ui/join-room-dialog.ui @@ -1,6 +1,6 @@ - + diff --git a/po/POTFILES.in b/po/POTFILES.in index f07245a2..383aeae5 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -85,6 +85,7 @@ src/session/content/room_history/typing_row.rs src/session/content/room_history/verification_info_bar.rs src/session/content/verification/identity_verification_widget.rs src/session/content/verification/session_verification.rs +src/session/join_room_dialog.rs src/session/mod.rs src/session/room/event/event_actions.rs src/session/room/member.rs diff --git a/src/session/join_room_dialog.rs b/src/session/join_room_dialog.rs new file mode 100644 index 00000000..0a53ff56 --- /dev/null +++ b/src/session/join_room_dialog.rs @@ -0,0 +1,187 @@ +use adw::{prelude::*, subclass::prelude::*}; +use gettextrs::gettext; +use gtk::{gdk, glib, CompositeTemplate}; +use ruma::{ + matrix_uri::MatrixId, MatrixToUri, MatrixUri, OwnedRoomOrAliasId, OwnedServerName, + RoomOrAliasId, +}; + +use crate::session::Session; + +mod imp { + use glib::{object::WeakRef, subclass::InitializingObject}; + + use super::*; + + #[derive(Debug, Default, CompositeTemplate)] + #[template(resource = "/org/gnome/Fractal/join-room-dialog.ui")] + pub struct JoinRoomDialog { + pub session: WeakRef, + #[template_child] + pub entry: TemplateChild, + } + + #[glib::object_subclass] + impl ObjectSubclass for JoinRoomDialog { + const NAME: &'static str = "JoinRoomDialog"; + type Type = super::JoinRoomDialog; + type ParentType = adw::MessageDialog; + + fn class_init(klass: &mut Self::Class) { + Self::bind_template(klass); + Self::Type::bind_template_callbacks(klass); + + klass.add_binding( + gdk::Key::Escape, + gdk::ModifierType::empty(), + |obj, _| { + obj.close(); + true + }, + None, + ); + } + + fn instance_init(obj: &InitializingObject) { + obj.init_template(); + } + } + + impl ObjectImpl for JoinRoomDialog { + fn properties() -> &'static [glib::ParamSpec] { + use once_cell::sync::Lazy; + static PROPERTIES: Lazy> = Lazy::new(|| { + vec![glib::ParamSpecObject::builder::("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!(), + } + } + } + + impl WidgetImpl for JoinRoomDialog {} + impl WindowImpl for JoinRoomDialog {} + + impl MessageDialogImpl for JoinRoomDialog { + fn response(&self, response: &str) { + self.obj().join_room(); + + self.parent_response(response) + } + } +} + +glib::wrapper! { + /// Dialog to join a room. + pub struct JoinRoomDialog(ObjectSubclass) + @extends gtk::Widget, gtk::Window, adw::MessageDialog, @implements gtk::Accessible; +} + +#[gtk::template_callbacks] +impl JoinRoomDialog { + pub fn new(parent_window: Option<&impl IsA>, session: &Session) -> Self { + glib::Object::builder() + .property("transient-for", parent_window) + .property("session", session) + .build() + } + + /// The current session. + pub fn session(&self) -> Option { + self.imp().session.upgrade() + } + + /// Set the current session. + pub fn set_session(&self, session: Option<&Session>) { + let imp = self.imp(); + + if self.session().as_ref() == session { + return; + } + + imp.session.set(session); + self.notify("session"); + } + + /// Handle when the entry text changed. + #[template_callback] + fn entry_changed(&self, entry: >k::Entry) { + let Some(session) = self.session() else { + self.set_response_enabled("join", false); + return; + }; + + let Some((room_id, _)) = parse_room(&entry.text()) else { + self.set_response_enabled("join", false); + return; + }; + + self.set_response_enabled("join", true); + + if session.room_list().find_joined_room(&room_id).is_some() { + self.set_response_label("join", &gettext("_View")); + } else { + self.set_response_label("join", &gettext("_Join")); + } + } + + /// Join the room that was entered, if it is valid. + fn join_room(&self) { + let Some(session) = self.session() else { + return; + }; + + let Some((room_id, via)) = parse_room(&self.imp().entry.text()) else { + return; + }; + + if let Some(room) = session.room_list().find_joined_room(&room_id) { + session.select_room(Some(room)); + } else { + session.room_list().join_by_id_or_alias(room_id, via) + } + } +} + +fn parse_room(room: &str) -> Option<(OwnedRoomOrAliasId, Vec)> { + MatrixUri::parse(room) + .ok() + .and_then(|uri| match uri.id() { + MatrixId::Room(room_id) => Some((room_id.clone().into(), uri.via().to_owned())), + MatrixId::RoomAlias(room_alias) => { + Some((room_alias.clone().into(), uri.via().to_owned())) + } + _ => None, + }) + .or_else(|| { + MatrixToUri::parse(room) + .ok() + .and_then(|uri| match uri.id() { + MatrixId::Room(room_id) => Some((room_id.clone().into(), uri.via().to_owned())), + MatrixId::RoomAlias(room_alias) => { + Some((room_alias.clone().into(), uri.via().to_owned())) + } + _ => None, + }) + }) + .or_else(|| { + RoomOrAliasId::parse(room) + .ok() + .map(|room_id| (room_id, vec![])) + }) +} diff --git a/src/session/mod.rs b/src/session/mod.rs index 9fda29be..0a8020e1 100644 --- a/src/session/mod.rs +++ b/src/session/mod.rs @@ -2,6 +2,7 @@ mod account_settings; mod avatar; mod content; mod event_source_dialog; +mod join_room_dialog; mod media_viewer; pub mod room; mod room_creation; @@ -36,9 +37,7 @@ use matrix_sdk::{ direct::DirectEventContent, room::encryption::SyncRoomEncryptionEvent, GlobalAccountDataEvent, }, - matrix_uri::MatrixId, - MatrixToUri, MatrixUri, OwnedEventId, OwnedRoomId, OwnedRoomOrAliasId, OwnedServerName, - RoomId, RoomOrAliasId, + OwnedEventId, OwnedRoomId, RoomId, }, sync::SyncResponse, Client, @@ -49,6 +48,7 @@ use tokio::task::JoinHandle; use self::{ account_settings::AccountSettings, content::{verification::SessionVerification, Content}, + join_room_dialog::JoinRoomDialog, media_viewer::MediaViewer, room_list::RoomList, sidebar::Sidebar, @@ -684,37 +684,8 @@ impl Session { } async fn show_join_room_dialog(&self) { - let builder = gtk::Builder::from_resource("/org/gnome/Fractal/join-room-dialog.ui"); - let dialog = builder.object::("dialog").unwrap(); - let entry = builder.object::("entry").unwrap(); - - entry.connect_changed(clone!(@weak self as obj, @weak dialog => move |entry| { - let room = parse_room(&entry.text()); - dialog.set_response_enabled("join", room.is_some()); - - if room - .and_then(|(room_id, _)| obj.room_list().find_joined_room(&room_id)) - .is_some() - { - dialog.set_response_label("join", &gettext("_View")); - } else { - dialog.set_response_label("join", &gettext("_Join")); - } - })); - - dialog.set_transient_for(self.parent_window().as_ref()); - if dialog.choose_future().await == "join" { - let (room_id, via) = match parse_room(&entry.text()) { - Some(room) => room, - None => return, - }; - - if let Some(room) = self.room_list().find_joined_room(&room_id) { - self.select_room(Some(room)); - } else { - self.room_list().join_by_id_or_alias(room_id, via) - } - } + let dialog = JoinRoomDialog::new(self.parent_window().as_ref(), self); + dialog.present(); } pub async fn logout(&self) { @@ -1004,34 +975,6 @@ impl Session { } } -fn parse_room(room: &str) -> Option<(OwnedRoomOrAliasId, Vec)> { - MatrixUri::parse(room) - .ok() - .and_then(|uri| match uri.id() { - MatrixId::Room(room_id) => Some((room_id.clone().into(), uri.via().to_owned())), - MatrixId::RoomAlias(room_alias) => { - Some((room_alias.clone().into(), uri.via().to_owned())) - } - _ => None, - }) - .or_else(|| { - MatrixToUri::parse(room) - .ok() - .and_then(|uri| match uri.id() { - MatrixId::Room(room_id) => Some((room_id.clone().into(), uri.via().to_owned())), - MatrixId::RoomAlias(room_alias) => { - Some((room_alias.clone().into(), uri.via().to_owned())) - } - _ => None, - }) - }) - .or_else(|| { - RoomOrAliasId::parse(room) - .ok() - .map(|room_id| (room_id, vec![])) - }) -} - fn notification_id(session_id: &str, room_id: &RoomId, event_id: &EventId) -> String { format!("{session_id}:{room_id}:{event_id}") }