diff --git a/data/resources/resources.gresource.xml b/data/resources/resources.gresource.xml index 58d0a9fb..1ab00387 100644 --- a/data/resources/resources.gresource.xml +++ b/data/resources/resources.gresource.xml @@ -112,6 +112,7 @@ ui/event-source-dialog.ui ui/greeter.ui ui/identity-verification-widget.ui + ui/join-room-dialog.ui ui/login-advanced-dialog.ui ui/login-idp-button.ui ui/login.ui diff --git a/data/resources/ui/join-room-dialog.ui b/data/resources/ui/join-room-dialog.ui new file mode 100644 index 00000000..d12ba2e7 --- /dev/null +++ b/data/resources/ui/join-room-dialog.ui @@ -0,0 +1,18 @@ + + + + Join a Room + Enter a room ID, room alias, or permalink. + join + cancel + + _Cancel + _Join + + + + True + + + + diff --git a/data/resources/ui/sidebar.ui b/data/resources/ui/sidebar.ui index 69b31d73..1b4c9461 100644 --- a/data/resources/ui/sidebar.ui +++ b/data/resources/ui/sidebar.ui @@ -6,6 +6,10 @@ _New Room session.room-creation + + _Join Room + session.show-join-room +
@@ -170,4 +174,3 @@ - diff --git a/po/POTFILES.in b/po/POTFILES.in index 78987dfa..abc18a60 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -34,6 +34,7 @@ data/resources/ui/event-menu.ui data/resources/ui/event-source-dialog.ui data/resources/ui/greeter.ui data/resources/ui/identity-verification-widget.ui +data/resources/ui/join-room-dialog.ui data/resources/ui/login-advanced-dialog.ui data/resources/ui/login.ui data/resources/ui/member-menu.ui diff --git a/src/session/content/explore/public_room.rs b/src/session/content/explore/public_room.rs index 9995251d..50b668b9 100644 --- a/src/session/content/explore/public_room.rs +++ b/src/session/content/explore/public_room.rs @@ -193,7 +193,7 @@ impl PublicRoom { } else if let Some(matrix_public_room) = self.matrix_public_room() { let room_id: &RoomId = matrix_public_room.room_id.as_ref(); self.room_list() - .join_by_id_or_alias(<&RoomOrAliasId>::from(room_id).to_owned()); + .join_by_id_or_alias(<&RoomOrAliasId>::from(room_id).to_owned(), vec![]); } } } diff --git a/src/session/mod.rs b/src/session/mod.rs index 53265685..78046968 100644 --- a/src/session/mod.rs +++ b/src/session/mod.rs @@ -13,16 +13,12 @@ pub mod verification; use std::{collections::HashSet, fs, path::PathBuf, time::Duration}; -use adw::subclass::prelude::BinImpl; +use adw::{prelude::*, subclass::prelude::*}; use futures::StreamExt; use gettextrs::gettext; use gtk::{ - self, gdk, gio, - gio::prelude::*, - glib, + self, gdk, gio, glib, glib::{clone, signal::SignalHandlerId}, - prelude::*, - subclass::prelude::*, CompositeTemplate, }; use log::{debug, error, warn}; @@ -44,7 +40,8 @@ use matrix_sdk::{ direct::DirectEventContent, room::encryption::SyncRoomEncryptionEvent, GlobalAccountDataEvent, }, - RoomId, + matrix_uri::MatrixId, + MatrixUri, OwnedRoomOrAliasId, OwnedServerName, RoomId, RoomOrAliasId, }, store::{MigrationConflictStrategy, OpenStoreError, SledStateStore}, Client, ClientBuildError, Error, HttpError, RumaApiError, StoreError, @@ -74,7 +71,7 @@ use crate::{ secret::{Secret, StoredSession}, session::sidebar::ItemList, spawn, spawn_tokio, toast, - utils::check_if_reachable, + utils::{check_if_reachable, parse_matrix_to_uri}, UserFacingError, Window, }; @@ -174,6 +171,12 @@ mod imp { session.show_room_creation_dialog(); }); + klass.install_action("session.show-join-room", None, move |widget, _, _| { + spawn!(clone!(@weak widget => async move { + widget.show_join_room_dialog().await; + })); + }); + klass.add_binding_action( gdk::Key::Escape, gdk::ModifierType::empty(), @@ -821,6 +824,40 @@ impl Session { window.show(); } + 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.run_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) + } + } + } + pub async fn logout(&self, cleanup: bool) { let stack = &self.imp().stack; self.emit_by_name::<()>("logged-out", &[]); @@ -1027,3 +1064,29 @@ async fn create_client( .await .map_err(Into::into) } + +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(|| { + parse_matrix_to_uri(room) + .ok() + .and_then(|(id, via)| match id { + MatrixId::Room(room_id) => Some((room_id.into(), via)), + MatrixId::RoomAlias(room_alias) => Some((room_alias.into(), via)), + _ => None, + }) + }) + .or_else(|| { + RoomOrAliasId::parse(room) + .ok() + .map(|room_id| (room_id, vec![])) + }) +} diff --git a/src/session/room/mod.rs b/src/session/room/mod.rs index e5256d5b..efbd5e09 100644 --- a/src/session/room/mod.rs +++ b/src/session/room/mod.rs @@ -466,6 +466,18 @@ impl Room { ); } + pub fn is_joined(&self) -> bool { + matches!( + self.category(), + RoomType::Favorite + | RoomType::Normal + | RoomType::LowPriority + | RoomType::Outdated + | RoomType::Space + | RoomType::Direct + ) + } + pub fn category(&self) -> RoomType { self.imp().category.get() } diff --git a/src/session/room_list.rs b/src/session/room_list.rs index 7c7dbf52..74c10347 100644 --- a/src/session/room_list.rs +++ b/src/session/room_list.rs @@ -5,7 +5,7 @@ use indexmap::map::IndexMap; use log::error; use matrix_sdk::{ deserialized_responses::Rooms as ResponseRooms, - ruma::{OwnedRoomId, OwnedRoomOrAliasId, RoomId, RoomOrAliasId}, + ruma::{OwnedRoomId, OwnedRoomOrAliasId, OwnedServerName, RoomId, RoomOrAliasId}, }; use crate::{ @@ -298,7 +298,7 @@ impl RoomList { } } - pub fn join_by_id_or_alias(&self, identifier: OwnedRoomOrAliasId) { + pub fn join_by_id_or_alias(&self, identifier: OwnedRoomOrAliasId, via: Vec) { let client = self.session().client(); let identifier_clone = identifier.clone(); @@ -306,7 +306,7 @@ impl RoomList { let handle = spawn_tokio!(async move { client - .join_room_by_id_or_alias(&identifier_clone, &[]) + .join_room_by_id_or_alias(&identifier_clone, &via) .await }); @@ -344,4 +344,22 @@ impl RoomList { None }) } + + pub fn find_joined_room(&self, room_id: &RoomOrAliasId) -> Option { + let room_id = room_id.as_str(); + self.imp() + .list + .borrow() + .values() + .find(|room| { + (room.room_id() == room_id + || room + .matrix_room() + .canonical_alias() + .filter(|id| id == room_id) + .is_some()) + && room.is_joined() + }) + .cloned() + } }