room: Expose the language of a room

This commit is contained in:
Kévin Commaille 2023-10-29 17:11:45 +01:00
parent b7e4c059e7
commit c4c74c8aba
No known key found for this signature in database
GPG Key ID: 29A48C1F03620416
4 changed files with 132 additions and 10 deletions

View File

@ -108,7 +108,7 @@ src/session/view/sidebar/room_row.rs
src/session/view/sidebar/row.rs
src/shortcuts.ui
src/user_facing_error.rs
src/utils/matrix.rs
src/utils/matrix/mod.rs
src/utils/media.rs
src/utils/message_dialog.rs
src/window.rs

View File

@ -21,6 +21,7 @@ use matrix_sdk::{
DisplayName, HttpError, Result as MatrixResult, RoomInfo, RoomMemberships, RoomState,
};
use ruma::{
api::client::config::set_room_account_data,
events::{
reaction::ReactionEventContent,
receipt::{ReceiptEventContent, ReceiptType},
@ -34,6 +35,7 @@ use ruma::{
AnyMessageLikeEventContent, AnyRoomAccountDataEvent, AnySyncStateEvent,
AnySyncTimelineEvent, SyncEphemeralRoomEvent, SyncStateEvent,
},
serde::Raw,
OwnedEventId, OwnedRoomId, OwnedUserId, RoomId,
};
use tracing::{debug, error, warn};
@ -53,7 +55,13 @@ use super::{
room_list::RoomMetainfo, AvatarData, AvatarImage, AvatarUriSource, IdentityVerification,
Session, SidebarItem, SidebarItemImpl, User,
};
use crate::{components::Pill, gettext_f, prelude::*, spawn, spawn_tokio};
use crate::{
components::Pill,
gettext_f,
prelude::*,
spawn, spawn_tokio,
utils::matrix::custom_events::{LanguageEvent, LanguageEventContent},
};
mod imp {
use std::cell::Cell;
@ -105,6 +113,8 @@ mod imp {
pub typing_list: TypingList,
/// Whether anyone can join this room.
pub is_join_rule_public: Cell<bool>,
/// The language to spell check in this room.
pub language: RefCell<Option<String>>,
}
#[glib::object_subclass]
@ -183,6 +193,9 @@ mod imp {
glib::ParamSpecBoolean::builder("is-join-rule-public")
.explicit_notify()
.build(),
glib::ParamSpecString::builder("language")
.read_only()
.build(),
]
});
@ -231,6 +244,7 @@ mod imp {
"encrypted" => obj.is_encrypted().to_value(),
"typing-list" => obj.typing_list().to_value(),
"is-join-rule-public" => obj.is_join_rule_public().to_value(),
"language" => obj.language().to_value(),
_ => unimplemented!(),
}
}
@ -360,6 +374,10 @@ impl Room {
self.setup_receipts();
self.setup_typing();
spawn!(clone!(@weak self as obj => async move {
obj.load_language().await;
}));
spawn!(clone!(@weak self as obj => async move {
obj.watch_room_info().await;
}));
@ -1314,21 +1332,28 @@ impl Room {
}
pub fn handle_left_response(&self, response_room: LeftRoom) {
self.update_for_room_account_data(response_room.account_data);
self.update_for_events(response_room.timeline.events);
}
pub fn handle_joined_response(&self, response_room: JoinedRoom) {
if response_room
.account_data
.iter()
.any(|e| matches!(e.deserialize(), Ok(AnyRoomAccountDataEvent::Tag(_))))
{
self.load_category();
}
self.update_for_room_account_data(response_room.account_data);
self.update_for_events(response_room.timeline.events);
}
fn update_for_room_account_data(&self, room_account_data: Vec<Raw<AnyRoomAccountDataEvent>>) {
for raw in room_account_data {
if let Ok(AnyRoomAccountDataEvent::Tag(_)) = raw.deserialize() {
self.load_category();
continue;
}
if let Ok(language) = raw.deserialize_as::<LanguageEvent>() {
self.set_language_inner(Some(language.content.input_language));
}
}
}
/// Connect to the signal sent when a room was forgotten.
pub fn connect_room_forgotten<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
self.connect_local("room-forgotten", true, move |values| {
@ -1701,4 +1726,85 @@ impl Room {
self.imp().is_join_rule_public.set(is_public);
self.notify("is-join-rule-public");
}
async fn load_language(&self) {
let matrix_room = self.matrix_room();
let handle = spawn_tokio!(async move {
matrix_room
.account_data_static::<LanguageEventContent>()
.await
});
let raw = match handle.await.unwrap() {
Ok(Some(raw)) => raw,
Ok(None) => return,
Err(error) => {
error!(room_id = %self.room_id(), "Failed to load language room account data: {error}");
return;
}
};
match raw.deserialize() {
Ok(ev) => {
self.set_language_inner(Some(ev.content.input_language));
}
Err(error) => {
error!(room_id = %self.room_id(), "Failed to deserialize language room account data: {error}");
}
}
}
/// The language to spell check in this room.
pub fn language(&self) -> Option<String> {
self.imp().language.borrow().clone()
}
/// Set the language to spell check in this room.
pub fn set_language(&self, language: Option<String>) {
if !self.set_language_inner(language.clone()) {
return;
}
if let Some(language) = language {
spawn!(clone!(@weak self as obj => async move {
obj.set_room_account_data_language(language).await;
}));
}
}
fn set_language_inner(&self, language: Option<String>) -> bool {
let imp = self.imp();
if imp.language.borrow().as_ref() == language.as_ref() {
return false;
}
imp.language.replace(language.clone());
self.notify("language");
true
}
async fn set_room_account_data_language(&self, input_language: String) {
let client = self.session().client();
let user_id = client.user_id().unwrap().to_owned();
let room_id = self.room_id().to_owned();
let request = match set_room_account_data::v3::Request::new(
user_id,
room_id,
&LanguageEventContent { input_language },
) {
Ok(req) => req,
Err(error) => {
error!(room_id = %self.room_id(), "Failed to build request for language room account data: {error}");
return;
}
};
let handle = spawn_tokio!(async move { client.send(request, None,).await });
if let Err(error) = handle.await.unwrap() {
error!(room_id = %self.room_id(), "Failed to send language room account data: {error}");
}
}
}

View File

@ -0,0 +1,14 @@
use ruma::events::macros::EventContent;
use serde::{Deserialize, Serialize};
/// The content of an `org.gnome.fractal.language` event.
///
/// The language used in a room.
///
/// It is used to change the spell checker's language per room.
#[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
#[ruma_event(type = "org.gnome.fractal.language", kind = RoomAccountData)]
pub struct LanguageEventContent {
/// The language to spell check.
pub input_language: String,
}

View File

@ -13,6 +13,8 @@ use ruma::{
};
use thiserror::Error;
pub mod custom_events;
use super::media::filename_for_mime;
use crate::{
components::{Pill, DEFAULT_PLACEHOLDER},