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}")
}