session: Allow to join a room by ID, alias or permalink

This commit is contained in:
Kévin Commaille 2022-10-04 16:25:05 +02:00 committed by Kévin Commaille
parent 23691dbf87
commit 5d7d49a973
8 changed files with 129 additions and 13 deletions

View file

@ -112,6 +112,7 @@
<file compressed="true" preprocess="xml-stripblanks" alias="event-source-dialog.ui">ui/event-source-dialog.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="greeter.ui">ui/greeter.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="identity-verification-widget.ui">ui/identity-verification-widget.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="join-room-dialog.ui">ui/join-room-dialog.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="login-advanced-dialog.ui">ui/login-advanced-dialog.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="login-idp-button.ui">ui/login-idp-button.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="login.ui">ui/login.ui</file>

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object class="AdwMessageDialog" id="dialog">
<property name="heading" translatable="yes">Join a Room</property>
<property name="body" translatable="yes">Enter a room ID, room alias, or permalink.</property>
<property name="default-response">join</property>
<property name="close-response">cancel</property>
<responses>
<response id="cancel" translatable="yes">_Cancel</response>
<response id="join" translatable="yes" appearance="suggested" enabled="false">_Join</response>
</responses>
<property name="extra-child">
<object class="GtkEntry" id="entry">
<property name="activates-default">True</property>
</object>
</property>
</object>
</interface>

View file

@ -6,6 +6,10 @@
<attribute name="label" translatable="yes">_New Room</attribute>
<attribute name="action">session.room-creation</attribute>
</item>
<item>
<attribute name="label" translatable="yes">_Join Room</attribute>
<attribute name="action">session.show-join-room</attribute>
</item>
</section>
<section>
<item>
@ -170,4 +174,3 @@
</child>
</template>
</interface>

View file

@ -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

View file

@ -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![]);
}
}
}

View file

@ -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::<adw::MessageDialog>("dialog").unwrap();
let entry = builder.object::<gtk::Entry>("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<OwnedServerName>)> {
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![]))
})
}

View file

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

View file

@ -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<OwnedServerName>) {
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<Room> {
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()
}
}