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 @@
+
+
+
+
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()
+ }
}