session: Do not use Session to show toasts
This usually means we are trying to show a toast from a model Object, which is not right.
This commit is contained in:
parent
e77fa4fedc
commit
4afba6e736
19 changed files with 426 additions and 246 deletions
|
@ -79,6 +79,7 @@ src/session/content/room_details/general_page/mod.rs
|
|||
src/session/content/room_details/history_viewer/audio_row.rs
|
||||
src/session/content/room_details/history_viewer/file_row.rs
|
||||
src/session/content/room_details/invite_subpage/invitee_list.rs
|
||||
src/session/content/room_details/invite_subpage/mod.rs
|
||||
src/session/content/room_details/member_page/mod.rs
|
||||
src/session/content/room_details/mod.rs
|
||||
src/session/content/room_history/item_row.rs
|
||||
|
@ -107,6 +108,8 @@ src/session/room_list.rs
|
|||
src/session/sidebar/category/category_row.rs
|
||||
src/session/sidebar/category/category_type.rs
|
||||
src/session/sidebar/entry_type.rs
|
||||
src/session/sidebar/room_row.rs
|
||||
src/session/sidebar/row.rs
|
||||
src/session/verification/identity_verification.rs
|
||||
src/user_facing_error.rs
|
||||
src/utils/media.rs
|
||||
|
|
|
@ -257,7 +257,9 @@ impl Login {
|
|||
/// Drop the session and clean up its data from the system.
|
||||
fn drop_session(&self) {
|
||||
if let Some(session) = self.imp().session.take() {
|
||||
glib::MainContext::default().block_on(session.logout());
|
||||
glib::MainContext::default().block_on(async move {
|
||||
let _ = session.logout().await;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -169,7 +169,13 @@ impl DeactivateAccountSubpage {
|
|||
match result {
|
||||
Ok(_) => {
|
||||
if let Some(session) = self.session() {
|
||||
toast!(session, gettext("Account successfully deactivated"));
|
||||
if let Some(window) = self
|
||||
.root()
|
||||
.and_downcast_ref::<gtk::Window>()
|
||||
.and_then(|w| w.transient_for())
|
||||
{
|
||||
toast!(window, gettext("Account successfully deactivated"));
|
||||
}
|
||||
session.handle_logged_out();
|
||||
}
|
||||
self.activate_action("account-settings.close", None)
|
||||
|
|
|
@ -4,7 +4,7 @@ use gtk::{
|
|||
CompositeTemplate,
|
||||
};
|
||||
|
||||
use crate::{components::SpinnerButton, session::Session, spawn};
|
||||
use crate::{components::SpinnerButton, session::Session, spawn, toast};
|
||||
|
||||
mod imp {
|
||||
use glib::{subclass::InitializingObject, WeakRef};
|
||||
|
@ -100,8 +100,11 @@ impl LogOutSubpage {
|
|||
make_backup_button.set_sensitive(false);
|
||||
|
||||
spawn!(
|
||||
clone!(@weak logout_button, @weak make_backup_button, @weak session => async move {
|
||||
session.logout().await;
|
||||
clone!(@weak self as obj, @weak logout_button, @weak make_backup_button, @weak session => async move {
|
||||
if let Err(error) = session.logout().await {
|
||||
toast!(obj, error);
|
||||
}
|
||||
|
||||
logout_button.set_loading(false);
|
||||
make_backup_button.set_sensitive(true);
|
||||
})
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use gtk::{glib, glib::clone, prelude::*, subclass::prelude::*};
|
||||
use matrix_sdk::ruma::{directory::PublicRoomsChunk, RoomId, RoomOrAliasId};
|
||||
use matrix_sdk::ruma::directory::PublicRoomsChunk;
|
||||
|
||||
use crate::session::{room::Room, AvatarData, AvatarImage, AvatarUriSource, RoomList};
|
||||
|
||||
|
@ -181,14 +181,4 @@ impl PublicRoom {
|
|||
pub fn matrix_public_room(&self) -> Option<&PublicRoomsChunk> {
|
||||
self.imp().matrix_public_room.get()
|
||||
}
|
||||
|
||||
pub fn join_or_view(&self) {
|
||||
if let Some(room) = self.room() {
|
||||
self.room_list().session().select_room(Some(room.clone()));
|
||||
} 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(), vec![]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ use gtk::{glib, glib::clone, prelude::*, subclass::prelude::*, CompositeTemplate
|
|||
use crate::{
|
||||
components::{Avatar, Spinner, SpinnerButton},
|
||||
session::content::explore::PublicRoom,
|
||||
spawn, toast,
|
||||
window::Window,
|
||||
};
|
||||
|
||||
mod imp {
|
||||
|
@ -79,9 +81,7 @@ mod imp {
|
|||
self.parent_constructed();
|
||||
self.button
|
||||
.connect_clicked(clone!(@weak self as imp => move |_| {
|
||||
if let Some(public_room) = &*imp.public_room.borrow() {
|
||||
public_room.join_or_view();
|
||||
};
|
||||
imp.obj().join_or_view();
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -213,4 +213,27 @@ impl PublicRoomRow {
|
|||
|
||||
button.set_loading(public_room.is_pending());
|
||||
}
|
||||
|
||||
/// Join or view the public room.
|
||||
pub fn join_or_view(&self) {
|
||||
let Some(public_room) = self.public_room() else {
|
||||
return;
|
||||
};
|
||||
let room_list = public_room.room_list();
|
||||
|
||||
if let Some(room) = public_room.room() {
|
||||
if let Some(window) = self.root().and_downcast::<Window>() {
|
||||
let session = room_list.session();
|
||||
window.show_room(session.session_id(), room.room_id());
|
||||
}
|
||||
} else if let Some(matrix_public_room) = public_room.matrix_public_room() {
|
||||
let room_id = matrix_public_room.room_id.clone();
|
||||
|
||||
spawn!(clone!(@weak self as obj, @weak room_list => async move {
|
||||
if let Err(error) = room_list.join_by_id_or_alias(room_id.into(), vec![]).await {
|
||||
toast!(obj, error);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
use adw::subclass::prelude::*;
|
||||
use gettextrs::gettext;
|
||||
use gtk::{glib, glib::clone, prelude::*, CompositeTemplate};
|
||||
|
||||
use crate::{
|
||||
components::{Avatar, LabelWithWidgets, Pill, SpinnerButton},
|
||||
gettext_f,
|
||||
session::room::{Room, RoomType},
|
||||
spawn,
|
||||
spawn, toast,
|
||||
};
|
||||
|
||||
mod imp {
|
||||
|
@ -217,6 +218,16 @@ impl Invite {
|
|||
clone!(@weak self as obj, @strong room => move || async move {
|
||||
let result = room.accept_invite().await;
|
||||
if result.is_err() {
|
||||
toast!(
|
||||
obj,
|
||||
gettext(
|
||||
// Translators: Do NOT translate the content between '{' and '}', this
|
||||
// is a variable name.
|
||||
"Failed to accept invitation for {room}. Try again later.",
|
||||
),
|
||||
@room,
|
||||
);
|
||||
|
||||
obj.imp().accept_requests.borrow_mut().remove(&room);
|
||||
obj.reset();
|
||||
}
|
||||
|
@ -239,6 +250,16 @@ impl Invite {
|
|||
clone!(@weak self as obj, @strong room => move || async move {
|
||||
let result = room.reject_invite().await;
|
||||
if result.is_err() {
|
||||
toast!(
|
||||
obj,
|
||||
gettext(
|
||||
// Translators: Do NOT translate the content between '{' and '}', this
|
||||
// is a variable name.
|
||||
"Failed to reject invitation for {room}. Try again later.",
|
||||
),
|
||||
@room,
|
||||
);
|
||||
|
||||
obj.imp().reject_requests.borrow_mut().remove(&room);
|
||||
obj.reset();
|
||||
}
|
||||
|
|
|
@ -246,7 +246,7 @@ impl InviteeList {
|
|||
if user.is_invited() && user.invite_exception().is_none() {
|
||||
obj.add_invitee(user.clone());
|
||||
} else {
|
||||
obj.remove_invitee(user.user_id())
|
||||
obj.remove_invitee(&user.user_id())
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
@ -365,8 +365,8 @@ impl InviteeList {
|
|||
.collect()
|
||||
}
|
||||
|
||||
fn remove_invitee(&self, user_id: OwnedUserId) {
|
||||
let removed = self.imp().invitee_list.borrow_mut().remove(&user_id);
|
||||
pub fn remove_invitee(&self, user_id: &UserId) {
|
||||
let removed = self.imp().invitee_list.borrow_mut().remove(user_id);
|
||||
if let Some(user) = removed {
|
||||
user.set_invited(false);
|
||||
self.emit_by_name::<()>("invitee-removed", &[&user]);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use adw::subclass::prelude::*;
|
||||
use gettextrs::ngettext;
|
||||
use gtk::{gdk, glib, glib::clone, prelude::*, CompositeTemplate};
|
||||
|
||||
mod invitee;
|
||||
|
@ -11,8 +12,8 @@ use self::{
|
|||
};
|
||||
use crate::{
|
||||
components::{Pill, Spinner, SpinnerButton},
|
||||
session::{Room, User},
|
||||
spawn,
|
||||
session::{Room, User, UserExt},
|
||||
spawn, toast,
|
||||
};
|
||||
|
||||
mod imp {
|
||||
|
@ -297,9 +298,16 @@ impl InviteSubpage {
|
|||
.ok()
|
||||
}
|
||||
|
||||
/// Invite the selected users to the room.
|
||||
fn invite(&self) {
|
||||
self.imp().invite_button.set_loading(true);
|
||||
|
||||
spawn!(clone!(@weak self as obj => async move {
|
||||
obj.invite_inner().await;
|
||||
}));
|
||||
}
|
||||
|
||||
async fn invite_inner(&self) {
|
||||
let Some(room) = self.room() else {
|
||||
return;
|
||||
};
|
||||
|
@ -312,11 +320,38 @@ impl InviteSubpage {
|
|||
.into_iter()
|
||||
.map(glib::object::Cast::upcast)
|
||||
.collect();
|
||||
spawn!(clone!(@weak self as obj => async move {
|
||||
room.invite(invitees.as_slice()).await;
|
||||
obj.close();
|
||||
obj.imp().invite_button.set_loading(false);
|
||||
}));
|
||||
|
||||
match room.invite(&invitees).await {
|
||||
Ok(()) => {
|
||||
self.close();
|
||||
}
|
||||
Err(failed_users) => {
|
||||
for invitee in &invitees {
|
||||
if !failed_users.contains(&invitee) {
|
||||
user_list.remove_invitee(&invitee.user_id())
|
||||
}
|
||||
}
|
||||
|
||||
let n = failed_users.len();
|
||||
let first_failed = failed_users.first().unwrap();
|
||||
|
||||
toast!(
|
||||
self,
|
||||
ngettext(
|
||||
// Translators: Do NOT translate the content between '{' and '}', these
|
||||
// are variable names.
|
||||
"Failed to invite {user} to {room}. Try again later.",
|
||||
"Failed to invite {n} users to {room}. Try again later.",
|
||||
n as u32,
|
||||
),
|
||||
@user = first_failed,
|
||||
@room,
|
||||
n = n.to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
self.imp().invite_button.set_loading(false);
|
||||
}
|
||||
|
||||
fn update_view(&self) {
|
||||
|
|
|
@ -178,8 +178,10 @@ mod imp {
|
|||
widget.send_text_message();
|
||||
},
|
||||
);
|
||||
klass.install_action("room-history.leave", None, move |widget, _, _| {
|
||||
widget.leave();
|
||||
klass.install_action("room-history.leave", None, move |obj, _, _| {
|
||||
spawn!(clone!(@weak obj => async move {
|
||||
obj.leave().await;
|
||||
}));
|
||||
});
|
||||
|
||||
klass.install_action("room-history.try-again", None, move |widget, _, _| {
|
||||
|
@ -779,9 +781,24 @@ impl RoomHistory {
|
|||
self.clear_related_event();
|
||||
}
|
||||
|
||||
pub fn leave(&self) {
|
||||
if let Some(room) = &*self.imp().room.borrow() {
|
||||
room.set_category(RoomType::Left);
|
||||
/// Leave the room.
|
||||
pub async fn leave(&self) {
|
||||
let Some(room) = self.room() else {
|
||||
return;
|
||||
};
|
||||
let previous_category = room.category();
|
||||
|
||||
if room.set_category(RoomType::Left).await.is_err() {
|
||||
toast!(
|
||||
self,
|
||||
gettext(
|
||||
// Translators: Do NOT translate the content between '{' and '}', this is a variable name.
|
||||
"Failed to move {room} from {previous_category} to {new_category}.",
|
||||
),
|
||||
@room,
|
||||
previous_category = previous_category.to_string(),
|
||||
new_category = RoomType::Left.to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1440,10 +1457,16 @@ impl RoomHistory {
|
|||
let Some(successor) = room.successor() else {
|
||||
return;
|
||||
};
|
||||
let successor = successor.to_owned();
|
||||
|
||||
room.session()
|
||||
.room_list()
|
||||
.join_by_id_or_alias(successor.to_owned().into(), vec![]);
|
||||
spawn!(clone!(@weak self as obj, @weak room => async move {
|
||||
if let Err(error) = room.session()
|
||||
.room_list()
|
||||
.join_by_id_or_alias(successor.into(), vec![]).await
|
||||
{
|
||||
toast!(obj, error);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use adw::{prelude::*, subclass::prelude::*};
|
|||
use gettextrs::gettext;
|
||||
use gtk::{glib, glib::clone, CompositeTemplate};
|
||||
|
||||
use crate::{session::Room, utils::BoundObjectWeakRef};
|
||||
use crate::{session::Room, spawn, toast, utils::BoundObjectWeakRef};
|
||||
|
||||
mod imp {
|
||||
use glib::subclass::InitializingObject;
|
||||
|
@ -133,9 +133,20 @@ impl StateTombstone {
|
|||
let Some(successor) = room.successor() else {
|
||||
return;
|
||||
};
|
||||
let session = room.session();
|
||||
let room_list = session.room_list();
|
||||
|
||||
room.session()
|
||||
.room_list()
|
||||
.join_or_view(successor.into(), vec![]);
|
||||
// Join or view the room with the given identifier.
|
||||
if let Some(successor_room) = room_list.joined_room(successor.into()) {
|
||||
session.select_room(Some(successor_room));
|
||||
} else {
|
||||
let successor = successor.to_owned();
|
||||
|
||||
spawn!(clone!(@weak self as obj, @weak room_list => async move {
|
||||
if let Err(error) = room_list.join_by_id_or_alias(successor.into(), vec![]).await {
|
||||
toast!(obj, error);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ impl DmUser {
|
|||
////
|
||||
/// If A DM chat exists already no new room is created and the existing one
|
||||
/// is returned.
|
||||
pub async fn start_chat(&self) -> Result<Room, matrix_sdk::Error> {
|
||||
pub async fn start_chat(&self) -> Result<Room, ()> {
|
||||
let session = self.session();
|
||||
let client = session.client();
|
||||
let other_user = self.user_id();
|
||||
|
@ -116,8 +116,11 @@ impl DmUser {
|
|||
// We can be sure that this room has only ourself and maybe the other user as
|
||||
// member.
|
||||
if room.matrix_room().active_members_count() < 2 {
|
||||
room.invite(&[self.clone().upcast()]).await;
|
||||
debug!("{other_user} left the chat, re-invite them");
|
||||
|
||||
if room.invite(&[self.clone().upcast()]).await.is_err() {
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(room);
|
||||
|
@ -137,7 +140,7 @@ impl DmUser {
|
|||
}
|
||||
Err(error) => {
|
||||
error!("Couldn’t create a new Direct Chat: {error}");
|
||||
Err(error)
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,11 @@ use adw::subclass::prelude::*;
|
|||
use gtk::{gdk, glib, glib::clone, prelude::*, CompositeTemplate};
|
||||
|
||||
mod dm_user;
|
||||
use self::dm_user::DmUser;
|
||||
mod dm_user_list;
|
||||
mod dm_user_row;
|
||||
|
||||
use self::{
|
||||
dm_user::DmUser,
|
||||
dm_user_list::{DmUserList, DmUserListState},
|
||||
dm_user_row::DmUserRow,
|
||||
};
|
||||
|
@ -177,23 +178,32 @@ impl CreateDmDialog {
|
|||
|
||||
#[template_callback]
|
||||
fn row_activated_cb(&self, row: gtk::ListBoxRow) {
|
||||
let Some(user): Option<DmUser> = row.downcast::<DmUserRow>().ok().and_then(|r| r.user()) else { return; };
|
||||
let Some(user) = row.downcast_ref::<DmUserRow>().and_then(|r| r.user()) else {
|
||||
return;
|
||||
};
|
||||
|
||||
// TODO: For now we show the loading page while we create the room,
|
||||
// ideally we would like to have the same behavior as Element:
|
||||
// Create the room only once the user sends a message
|
||||
self.imp().stack.set_visible_child_name("loading-page");
|
||||
self.imp().search_entry.set_sensitive(false);
|
||||
let imp = self.imp();
|
||||
imp.stack.set_visible_child_name("loading-page");
|
||||
imp.search_entry.set_sensitive(false);
|
||||
|
||||
spawn!(clone!(@weak self as obj, @weak user => async move {
|
||||
match user.start_chat().await {
|
||||
Ok(room) => {
|
||||
user.session().select_room(Some(room));
|
||||
obj.close();
|
||||
}
|
||||
Err(_) => {
|
||||
obj.show_error(&gettext("Failed to create a new Direct Chat"));
|
||||
}
|
||||
}
|
||||
obj.start_chat(&user).await;
|
||||
}));
|
||||
}
|
||||
|
||||
async fn start_chat(&self, user: &DmUser) {
|
||||
match user.start_chat().await {
|
||||
Ok(room) => {
|
||||
user.session().select_room(Some(room));
|
||||
self.close();
|
||||
}
|
||||
Err(_) => {
|
||||
self.show_error(&gettext("Failed to create a new Direct Chat"));
|
||||
self.imp().search_entry.set_sensitive(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use adw::{prelude::*, subclass::prelude::*};
|
||||
use gettextrs::gettext;
|
||||
use gtk::{gdk, glib, CompositeTemplate};
|
||||
use gtk::{gdk, glib, glib::clone, CompositeTemplate};
|
||||
use ruma::{
|
||||
matrix_uri::MatrixId, MatrixToUri, MatrixUri, OwnedRoomOrAliasId, OwnedServerName,
|
||||
RoomOrAliasId,
|
||||
};
|
||||
|
||||
use crate::session::Session;
|
||||
use crate::{session::Session, spawn, toast};
|
||||
|
||||
mod imp {
|
||||
use glib::{object::WeakRef, subclass::InitializingObject};
|
||||
|
@ -148,15 +148,25 @@ impl JoinRoomDialog {
|
|||
|
||||
/// 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;
|
||||
};
|
||||
|
||||
session.room_list().join_or_view((&*room_id).into(), via);
|
||||
let Some(session) = self.session() else {
|
||||
return;
|
||||
};
|
||||
let room_list = session.room_list();
|
||||
|
||||
// Join or view the room with the given identifier.
|
||||
if let Some(room) = room_list.joined_room((&*room_id).into()) {
|
||||
session.select_room(Some(room));
|
||||
} else {
|
||||
spawn!(clone!(@weak self as obj, @weak room_list => async move {
|
||||
if let Err(error) = room_list.join_by_id_or_alias(room_id, via).await {
|
||||
toast!(obj, error);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ pub use self::{
|
|||
use crate::{
|
||||
secret::StoredSession,
|
||||
session::sidebar::ItemList,
|
||||
spawn, spawn_tokio, toast,
|
||||
spawn, spawn_tokio,
|
||||
utils::{
|
||||
check_if_reachable,
|
||||
matrix::{self, ClientSetupError},
|
||||
|
@ -681,7 +681,7 @@ impl Session {
|
|||
dialog.present();
|
||||
}
|
||||
|
||||
pub async fn logout(&self) {
|
||||
pub async fn logout(&self) -> Result<(), String> {
|
||||
let stack = &self.imp().stack;
|
||||
|
||||
debug!("The session is about to be logged out");
|
||||
|
@ -698,10 +698,15 @@ impl Session {
|
|||
});
|
||||
|
||||
match handle.await.unwrap() {
|
||||
Ok(_) => self.cleanup_session().await,
|
||||
Ok(_) => {
|
||||
self.cleanup_session().await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Err(error) => {
|
||||
error!("Couldn’t logout the session: {error}");
|
||||
toast!(self, gettext("Failed to logout the session."));
|
||||
|
||||
Err(gettext("Failed to logout the session."))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ mod typing_list;
|
|||
|
||||
use std::{cell::RefCell, io::Cursor};
|
||||
|
||||
use gettextrs::{gettext, ngettext};
|
||||
use gettextrs::gettext;
|
||||
use gtk::{glib, glib::clone, prelude::*, subclass::prelude::*};
|
||||
use log::{debug, error, warn};
|
||||
use matrix_sdk::{
|
||||
|
@ -58,7 +58,7 @@ use crate::{
|
|||
sidebar::{SidebarItem, SidebarItemImpl},
|
||||
AvatarData, AvatarImage, AvatarUriSource, Session, User,
|
||||
},
|
||||
spawn, spawn_tokio, toast,
|
||||
spawn, spawn_tokio,
|
||||
};
|
||||
|
||||
mod imp {
|
||||
|
@ -143,7 +143,7 @@ mod imp {
|
|||
.read_only()
|
||||
.build(),
|
||||
glib::ParamSpecEnum::builder::<RoomType>("category")
|
||||
.explicit_notify()
|
||||
.read_only()
|
||||
.build(),
|
||||
glib::ParamSpecString::builder("topic").read_only().build(),
|
||||
glib::ParamSpecUInt64::builder("latest-unread")
|
||||
|
@ -184,7 +184,6 @@ mod imp {
|
|||
|
||||
match pspec.name() {
|
||||
"session" => self.session.set(value.get().ok().as_ref()),
|
||||
"category" => obj.set_category(value.get().unwrap()),
|
||||
"room-id" => self
|
||||
.room_id
|
||||
.set(RoomId::parse(value.get::<&str>().unwrap()).unwrap())
|
||||
|
@ -299,18 +298,13 @@ impl Room {
|
|||
self.imp().room_id.get().unwrap()
|
||||
}
|
||||
|
||||
/// Set the proper category for this joined room.
|
||||
pub fn set_joined(&self) {
|
||||
/// Whether this room is direct or not.
|
||||
pub async fn is_direct(&self) -> bool {
|
||||
let matrix_room = self.matrix_room();
|
||||
|
||||
let handle = spawn_tokio!(async move { matrix_room.is_direct().await.unwrap_or_default() });
|
||||
spawn!(clone!(@weak self as obj => async move {
|
||||
if handle.await.unwrap() {
|
||||
obj.set_category(RoomType::Direct);
|
||||
} else {
|
||||
obj.set_category(RoomType::Normal);
|
||||
}
|
||||
}));
|
||||
spawn_tokio!(async move { matrix_room.is_direct().await.unwrap_or_default() })
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn matrix_room(&self) -> MatrixRoom {
|
||||
|
@ -346,10 +340,10 @@ impl Room {
|
|||
}
|
||||
|
||||
/// Forget a room that is left.
|
||||
pub fn forget(&self) {
|
||||
pub async fn forget(&self) -> MatrixResult<()> {
|
||||
if self.category() != RoomType::Left {
|
||||
warn!("Cannot forget a room that is not left");
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let matrix_room = self.matrix_room();
|
||||
|
@ -361,29 +355,20 @@ impl Room {
|
|||
}
|
||||
});
|
||||
|
||||
spawn!(
|
||||
glib::PRIORITY_DEFAULT_IDLE,
|
||||
clone!(@weak self as obj => async move {
|
||||
match handle.await.unwrap() {
|
||||
Ok(_) => {
|
||||
obj.emit_by_name::<()>("room-forgotten", &[]);
|
||||
}
|
||||
Err(error) => {
|
||||
error!("Couldn’t forget the room: {error}");
|
||||
match handle.await.unwrap() {
|
||||
Ok(_) => {
|
||||
self.emit_by_name::<()>("room-forgotten", &[]);
|
||||
Ok(())
|
||||
}
|
||||
Err(error) => {
|
||||
error!("Couldn’t forget the room: {error}");
|
||||
|
||||
toast!(
|
||||
obj.session(),
|
||||
// Translators: Do NOT translate the content between '{' and '}', this is a variable name.
|
||||
gettext("Failed to forget {room}."),
|
||||
@room = &obj,
|
||||
);
|
||||
// Load the previous category
|
||||
self.load_category();
|
||||
|
||||
// Load the previous category
|
||||
obj.load_category();
|
||||
},
|
||||
};
|
||||
})
|
||||
);
|
||||
Err(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_joined(&self) -> bool {
|
||||
|
@ -417,32 +402,34 @@ impl Room {
|
|||
///
|
||||
/// Note: Rooms can't be moved to the invite category and they can't be
|
||||
/// moved once they are upgraded.
|
||||
pub fn set_category(&self, category: RoomType) {
|
||||
pub async fn set_category(&self, category: RoomType) -> MatrixResult<()> {
|
||||
let matrix_room = self.matrix_room();
|
||||
let previous_category = self.category();
|
||||
|
||||
if previous_category == category {
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if previous_category == RoomType::Outdated {
|
||||
warn!("Can't set the category of an upgraded room");
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
match category {
|
||||
RoomType::Invited => {
|
||||
warn!("Rooms can’t be moved to the invite Category");
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
RoomType::Outdated => {
|
||||
// Outdated rooms don't need to propagate anything to the server
|
||||
self.set_category_internal(category);
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
self.set_category_internal(category);
|
||||
|
||||
let handle = spawn_tokio!(async move {
|
||||
match matrix_room {
|
||||
MatrixRoom::Invited(room) => match category {
|
||||
|
@ -615,33 +602,17 @@ impl Room {
|
|||
Result::<_, matrix_sdk::Error>::Ok(())
|
||||
});
|
||||
|
||||
spawn!(
|
||||
glib::PRIORITY_DEFAULT_IDLE,
|
||||
clone!(@weak self as obj => async move {
|
||||
match handle.await.unwrap() {
|
||||
Ok(_) => {},
|
||||
Err(error) => {
|
||||
error!("Couldn’t set the room category: {error}");
|
||||
match handle.await.unwrap() {
|
||||
Ok(_) => Ok(()),
|
||||
Err(error) => {
|
||||
error!("Couldn’t set the room category: {error}");
|
||||
|
||||
toast!(
|
||||
obj.session(),
|
||||
gettext(
|
||||
// Translators: Do NOT translate the content between '{' and '}', this is a variable name.
|
||||
"Failed to move {room} from {previous_category} to {new_category}.",
|
||||
),
|
||||
@room = obj,
|
||||
previous_category = previous_category.to_string(),
|
||||
new_category = category.to_string(),
|
||||
);
|
||||
// Load the previous category
|
||||
self.load_category();
|
||||
|
||||
// Load the previous category
|
||||
obj.load_category();
|
||||
},
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
self.set_category_internal(category);
|
||||
Err(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_category(&self) {
|
||||
|
@ -1303,17 +1274,6 @@ impl Room {
|
|||
Ok(_) => Ok(()),
|
||||
Err(error) => {
|
||||
error!("Accepting invitation failed: {error}");
|
||||
|
||||
toast!(
|
||||
self.session(),
|
||||
gettext(
|
||||
// Translators: Do NOT translate the content between '{' and '}', this
|
||||
// is a variable name.
|
||||
"Failed to accept invitation for {room}. Try again later.",
|
||||
),
|
||||
@room = self,
|
||||
);
|
||||
|
||||
Err(error)
|
||||
}
|
||||
}
|
||||
|
@ -1333,16 +1293,6 @@ impl Room {
|
|||
Err(error) => {
|
||||
error!("Rejecting invitation failed: {error}");
|
||||
|
||||
toast!(
|
||||
self.session(),
|
||||
gettext(
|
||||
// Translators: Do NOT translate the content between '{' and '}', this
|
||||
// is a variable name.
|
||||
"Failed to reject invitation for {room}. Try again later.",
|
||||
),
|
||||
@room = self,
|
||||
);
|
||||
|
||||
Err(error)
|
||||
}
|
||||
}
|
||||
|
@ -1525,21 +1475,25 @@ impl Room {
|
|||
});
|
||||
}
|
||||
|
||||
pub async fn invite(&self, users: &[User]) {
|
||||
/// Invite the given users to this room.
|
||||
///
|
||||
/// Returns `Ok(())` if all the invites are sent successfully, otherwise
|
||||
/// returns the list of users who could not be invited.
|
||||
pub async fn invite<'a>(&self, users: &'a [User]) -> Result<(), Vec<&'a User>> {
|
||||
let MatrixRoom::Joined(matrix_room) = self.matrix_room() else {
|
||||
error!("Can’t invite users, because this room isn’t a joined room");
|
||||
return;
|
||||
return Ok(());
|
||||
};
|
||||
let user_ids: Vec<OwnedUserId> = users.iter().map(|user| user.user_id()).collect();
|
||||
|
||||
let handle = spawn_tokio!(async move {
|
||||
let invitiations = user_ids
|
||||
let invitations = user_ids
|
||||
.iter()
|
||||
.map(|user_id| matrix_room.invite_user_by_id(user_id));
|
||||
futures::future::join_all(invitiations).await
|
||||
futures::future::join_all(invitations).await
|
||||
});
|
||||
|
||||
let mut failed_invites: Vec<User> = Vec::new();
|
||||
let mut failed_invites = Vec::new();
|
||||
for (index, result) in handle.await.unwrap().iter().enumerate() {
|
||||
match result {
|
||||
Ok(_) => {}
|
||||
|
@ -1548,43 +1502,15 @@ impl Room {
|
|||
"Failed to invite user with id {}: {error}",
|
||||
users[index].user_id(),
|
||||
);
|
||||
failed_invites.push(users[index].clone());
|
||||
failed_invites.push(&users[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !failed_invites.is_empty() {
|
||||
let no_failed = failed_invites.len();
|
||||
let first_failed = failed_invites.first().unwrap();
|
||||
|
||||
// TODO: should we show all the failed users?
|
||||
if no_failed == 1 {
|
||||
toast!(
|
||||
self.session(),
|
||||
gettext(
|
||||
// Translators: Do NOT translate the content between '{' and '}', this
|
||||
// is a variable name.
|
||||
"Failed to invite {user} to {room}. Try again later.",
|
||||
),
|
||||
@user = first_failed,
|
||||
@room = self,
|
||||
);
|
||||
} else {
|
||||
let n = (no_failed - 1) as u32;
|
||||
toast!(
|
||||
self.session(),
|
||||
ngettext(
|
||||
// Translators: Do NOT translate the content between '{' and '}', this
|
||||
// is a variable name.
|
||||
"Failed to invite {user} and 1 other user to {room}. Try again later.",
|
||||
"Failed to invite {user} and {n} other users to {room}. Try again later.",
|
||||
n,
|
||||
),
|
||||
@user = first_failed,
|
||||
@room = self,
|
||||
n = n.to_string(),
|
||||
);
|
||||
};
|
||||
if failed_invites.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(failed_invites)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ use matrix_sdk::{
|
|||
use crate::{
|
||||
gettext_f,
|
||||
session::{room::Room, Session},
|
||||
spawn, spawn_tokio, toast,
|
||||
spawn_tokio,
|
||||
};
|
||||
|
||||
mod imp {
|
||||
|
@ -314,7 +314,11 @@ impl RoomList {
|
|||
}
|
||||
|
||||
/// Join the room with the given identifier.
|
||||
pub fn join_by_id_or_alias(&self, identifier: OwnedRoomOrAliasId, via: Vec<OwnedServerName>) {
|
||||
pub async fn join_by_id_or_alias(
|
||||
&self,
|
||||
identifier: OwnedRoomOrAliasId,
|
||||
via: Vec<OwnedServerName>,
|
||||
) -> Result<(), String> {
|
||||
let client = self.session().client();
|
||||
let identifier_clone = identifier.clone();
|
||||
|
||||
|
@ -326,26 +330,25 @@ impl RoomList {
|
|||
.await
|
||||
});
|
||||
|
||||
spawn!(
|
||||
glib::PRIORITY_DEFAULT_IDLE,
|
||||
clone!(@weak self as obj => async move {
|
||||
match handle.await.unwrap() {
|
||||
Ok(matrix_room) => obj.pending_rooms_replace_or_remove(&identifier, matrix_room.room_id()),
|
||||
Err(error) => {
|
||||
obj.pending_rooms_remove(&identifier);
|
||||
error!("Joining room {identifier} failed: {error}");
|
||||
match handle.await.unwrap() {
|
||||
Ok(matrix_room) => {
|
||||
self.pending_rooms_replace_or_remove(&identifier, matrix_room.room_id());
|
||||
Ok(())
|
||||
}
|
||||
Err(error) => {
|
||||
self.pending_rooms_remove(&identifier);
|
||||
error!("Joining room {identifier} failed: {error}");
|
||||
|
||||
let error = gettext_f(
|
||||
// Translators: Do NOT translate the content between '{' and '}', this is a variable name.
|
||||
"Failed to join room {room_name}. Try again later.",
|
||||
&[("room_name", identifier.as_str())]
|
||||
);
|
||||
let error = gettext_f(
|
||||
// Translators: Do NOT translate the content between '{' and '}', this is a
|
||||
// variable name.
|
||||
"Failed to join room {room_name}. Try again later.",
|
||||
&[("room_name", identifier.as_str())],
|
||||
);
|
||||
|
||||
toast!(obj.session(), error);
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
Err(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn connect_pending_rooms_changed<F: Fn(&Self) + 'static>(
|
||||
|
@ -367,15 +370,6 @@ impl RoomList {
|
|||
.filter(|room| room.is_joined())
|
||||
}
|
||||
|
||||
/// Join or view the room with the given identifier.
|
||||
pub fn join_or_view(&self, identifier: RoomIdentifier, via: Vec<OwnedServerName>) {
|
||||
if let Some(room) = self.joined_room(identifier) {
|
||||
self.session().select_room(Some(room));
|
||||
} else {
|
||||
self.join_by_id_or_alias(identifier.into(), via);
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a room that was tombstoned but for which we haven't joined the
|
||||
/// successor yet.
|
||||
pub fn add_tombstoned_room(&self, room_id: OwnedRoomId) {
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
use adw::subclass::prelude::BinImpl;
|
||||
use gettextrs::gettext;
|
||||
use gtk::{gdk, glib, glib::clone, prelude::*, subclass::prelude::*, CompositeTemplate};
|
||||
|
||||
use super::Row;
|
||||
use crate::{
|
||||
components::{ContextMenuBin, ContextMenuBinExt, ContextMenuBinImpl},
|
||||
session::room::{HighlightFlags, Room, RoomType},
|
||||
spawn, toast,
|
||||
};
|
||||
|
||||
mod imp {
|
||||
|
@ -39,34 +41,52 @@ mod imp {
|
|||
|
||||
klass.set_accessible_role(gtk::AccessibleRole::Group);
|
||||
|
||||
klass.install_action("room-row.accept-invite", None, move |widget, _, _| {
|
||||
widget.set_room_as_normal_or_direct();
|
||||
klass.install_action("room-row.accept-invite", None, move |obj, _, _| {
|
||||
spawn!(clone!(@weak obj => async move {
|
||||
obj.set_room_as_normal_or_direct().await;
|
||||
}));
|
||||
});
|
||||
klass.install_action("room-row.reject-invite", None, move |widget, _, _| {
|
||||
widget.room().unwrap().set_category(RoomType::Left)
|
||||
klass.install_action("room-row.reject-invite", None, move |obj, _, _| {
|
||||
spawn!(clone!(@weak obj => async move {
|
||||
obj.set_category(RoomType::Left).await
|
||||
}));
|
||||
});
|
||||
|
||||
klass.install_action("room-row.set-favorite", None, move |widget, _, _| {
|
||||
widget.room().unwrap().set_category(RoomType::Favorite);
|
||||
klass.install_action("room-row.set-favorite", None, move |obj, _, _| {
|
||||
spawn!(clone!(@weak obj => async move {
|
||||
obj.set_category(RoomType::Favorite).await
|
||||
}));
|
||||
});
|
||||
klass.install_action("room-row.set-normal", None, move |widget, _, _| {
|
||||
widget.room().unwrap().set_category(RoomType::Normal);
|
||||
klass.install_action("room-row.set-normal", None, move |obj, _, _| {
|
||||
spawn!(clone!(@weak obj => async move {
|
||||
obj.set_category(RoomType::Normal).await
|
||||
}));
|
||||
});
|
||||
klass.install_action("room-row.set-lowpriority", None, move |widget, _, _| {
|
||||
widget.room().unwrap().set_category(RoomType::LowPriority);
|
||||
klass.install_action("room-row.set-lowpriority", None, move |obj, _, _| {
|
||||
spawn!(clone!(@weak obj => async move {
|
||||
obj.set_category(RoomType::LowPriority).await
|
||||
}));
|
||||
});
|
||||
klass.install_action("room-row.set-direct", None, move |widget, _, _| {
|
||||
widget.room().unwrap().set_category(RoomType::Direct);
|
||||
klass.install_action("room-row.set-direct", None, move |obj, _, _| {
|
||||
spawn!(clone!(@weak obj => async move {
|
||||
obj.set_category(RoomType::Direct).await
|
||||
}));
|
||||
});
|
||||
|
||||
klass.install_action("room-row.leave", None, move |widget, _, _| {
|
||||
widget.room().unwrap().set_category(RoomType::Left);
|
||||
klass.install_action("room-row.leave", None, move |obj, _, _| {
|
||||
spawn!(clone!(@weak obj => async move {
|
||||
obj.set_category(RoomType::Left).await
|
||||
}));
|
||||
});
|
||||
klass.install_action("room-row.join", None, move |widget, _, _| {
|
||||
widget.set_room_as_normal_or_direct();
|
||||
klass.install_action("room-row.join", None, move |obj, _, _| {
|
||||
spawn!(clone!(@weak obj => async move {
|
||||
obj.set_room_as_normal_or_direct().await;
|
||||
}));
|
||||
});
|
||||
klass.install_action("room-row.forget", None, move |widget, _, _| {
|
||||
widget.room().unwrap().forget();
|
||||
klass.install_action("room-row.forget", None, move |obj, _, _| {
|
||||
spawn!(clone!(@weak obj => async move {
|
||||
obj.forget().await
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -350,7 +370,66 @@ impl RoomRow {
|
|||
row.remove_css_class("drag");
|
||||
}
|
||||
|
||||
fn set_room_as_normal_or_direct(&self) {
|
||||
self.room().unwrap().set_joined();
|
||||
async fn set_room_as_normal_or_direct(&self) {
|
||||
let Some(room) = self.room() else {
|
||||
return;
|
||||
};
|
||||
let previous_category = room.category();
|
||||
|
||||
let category = if room.is_direct().await {
|
||||
RoomType::Direct
|
||||
} else {
|
||||
RoomType::Normal
|
||||
};
|
||||
|
||||
if room.set_category(category).await.is_err() {
|
||||
toast!(
|
||||
self,
|
||||
gettext(
|
||||
// Translators: Do NOT translate the content between '{' and '}', this is a variable name.
|
||||
"Failed to move {room} from {previous_category} to {new_category}.",
|
||||
),
|
||||
@room,
|
||||
previous_category = previous_category.to_string(),
|
||||
new_category = category.to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Change the category of this room.
|
||||
async fn set_category(&self, category: RoomType) {
|
||||
let Some(room) = self.room() else {
|
||||
return;
|
||||
};
|
||||
let previous_category = room.category();
|
||||
|
||||
if room.set_category(category).await.is_err() {
|
||||
toast!(
|
||||
self,
|
||||
gettext(
|
||||
// Translators: Do NOT translate the content between '{' and '}', this is a variable name.
|
||||
"Failed to move {room} from {previous_category} to {new_category}.",
|
||||
),
|
||||
@room,
|
||||
previous_category = previous_category.to_string(),
|
||||
new_category = category.to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Forget this room.
|
||||
async fn forget(&self) {
|
||||
let Some(room) = self.room() else {
|
||||
return;
|
||||
};
|
||||
|
||||
if room.forget().await.is_err() {
|
||||
toast!(
|
||||
self,
|
||||
// Translators: Do NOT translate the content between '{' and '}', this is a variable name.
|
||||
gettext("Failed to forget {room}."),
|
||||
@room,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use adw::{prelude::*, subclass::prelude::*};
|
||||
use gettextrs::gettext;
|
||||
use gtk::{gdk, glib, glib::clone};
|
||||
|
||||
use super::{CategoryType, EntryType};
|
||||
|
@ -10,6 +11,7 @@ use crate::{
|
|||
},
|
||||
verification::IdentityVerification,
|
||||
},
|
||||
spawn, toast,
|
||||
utils::BoundObjectWeakRef,
|
||||
};
|
||||
|
||||
|
@ -312,12 +314,16 @@ impl Row {
|
|||
if let Ok(room) = value.get::<Room>() {
|
||||
if let Some(target_type) = self.room_type() {
|
||||
if room.category().can_change_to(target_type) {
|
||||
room.set_category(target_type);
|
||||
spawn!(clone!(@weak self as obj, @weak room => async move {
|
||||
obj.set_room_category(&room, target_type).await;
|
||||
}));
|
||||
ret = true;
|
||||
}
|
||||
} else if let Some(entry_type) = self.entry_type() {
|
||||
if room.category() == RoomType::Left && entry_type == EntryType::Forget {
|
||||
room.forget();
|
||||
spawn!(clone!(@weak self as obj, @weak room => async move {
|
||||
obj.forget_room(&room).await;
|
||||
}));
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
|
@ -326,6 +332,36 @@ impl Row {
|
|||
ret
|
||||
}
|
||||
|
||||
/// Change the category of the given room room.
|
||||
async fn set_room_category(&self, room: &Room, category: RoomType) {
|
||||
let previous_category = room.category();
|
||||
|
||||
if room.set_category(category).await.is_err() {
|
||||
toast!(
|
||||
self,
|
||||
gettext(
|
||||
// Translators: Do NOT translate the content between '{' and '}', this is a variable name.
|
||||
"Failed to move {room} from {previous_category} to {new_category}.",
|
||||
),
|
||||
@room,
|
||||
previous_category = previous_category.to_string(),
|
||||
new_category = category.to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Forget the given room.
|
||||
async fn forget_room(&self, room: &Room) {
|
||||
if room.forget().await.is_err() {
|
||||
toast!(
|
||||
self,
|
||||
// Translators: Do NOT translate the content between '{' and '}', this is a variable name.
|
||||
gettext("Failed to forget {room}."),
|
||||
@room,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the disabled or empty state of this drop target.
|
||||
fn update_for_drop_source_type(&self) {
|
||||
let source_type = self.sidebar().drop_source_type();
|
||||
|
|
Loading…
Reference in a new issue