Commit d78b52a3 authored by Sonja Heinze's avatar Sonja Heinze Committed by Daniel Garcia Moreno

Save language chosen for spell check for each room

Before, when a user changed the language for spell check,
that language was saved by gtk application-wide.

With this commit, the language chosen by the user for spell check gets saved
in the Matrix account data of the particular room the language was chosen in;
and, by syncronizing, it also gets saved in the new field `language` of Room.
When the user enters a room, gspell gets set for the language saved
for that room.
parent 1dac3f21
Pipeline #135230 passed with stages
in 43 minutes and 24 seconds
......@@ -236,6 +236,9 @@ pub fn backend_loop(rx: Receiver<BKResponse>) {
APPOP!(show_error, (error));
APPOP!(set_state, (state));
}
BKResponse::ChangeLanguage(Err(err)) => {
error!("Error forming url to set room language: {:?}", err);
}
BKResponse::LoginError(_) => {
let error = i18n("Can’t login, try again");
let st = AppState::Login;
......
use crate::app::App;
use crate::backend::BKCommand;
use gtk::prelude::*;
// The TextBufferExt alias is necessary to avoid conflict with gtk's TextBufferExt
use gspell::{CheckerExt, TextBuffer, TextBufferExt as GspellTextBufferExt};
impl App {
pub fn connect_language(&self) {
let textview = self.ui.sventry.view.upcast_ref::<gtk::TextView>();
if let Some(checker) = textview
.get_buffer()
.and_then(|gtk_buffer| TextBuffer::get_from_gtk_text_buffer(&gtk_buffer))
.and_then(|gs_buffer| gs_buffer.get_spell_checker())
{
let op = self.op.clone();
let _signal_handler = checker.connect_property_language_notify(move |checker| {
if let Some(lang_code) = checker
.get_language()
.and_then(|lang| lang.get_code())
.map(|lang_code| String::from(lang_code))
{
/*If the checker is modified by fn set_language in fractal-gtk/src/appop/room.rs
due to the user switching rooms, the op mutex is locked already.
If the checker is modified by gtk due to the user switching the language, the op mutex is unlocked. */
if let Ok(op) = op.try_lock() {
if let Some(active_room) = &op.active_room {
let server = &op.server_url;
let access_token = unwrap_or_unit_return!(op.access_token.clone());
op.backend
.send(BKCommand::ChangeLanguage(access_token, server.clone(), lang_code, active_room.clone()))
.unwrap();
}
}
}
});
}
}
}
......@@ -5,6 +5,7 @@ mod directory;
mod headerbar;
mod invite;
mod join_room;
mod language;
mod leave_room;
mod markdown;
mod new_room;
......@@ -20,6 +21,7 @@ impl App {
self.connect_send();
self.connect_markdown();
self.connect_autocomplete();
self.connect_language();
self.connect_directory();
self.connect_leave_room_dialog();
......
......@@ -34,7 +34,7 @@ mod member;
mod message;
mod notifications;
mod notify;
mod room;
pub mod room;
mod room_settings;
mod start_chat;
pub mod state;
......
......@@ -27,6 +27,9 @@ use rand::{thread_rng, Rng};
use glib::functions::markup_escape_text;
// The TextBufferExt alias is necessary to avoid conflict with gtk's TextBufferExt
use gspell::{CheckerExt, TextBuffer, TextBufferExt as GspellTextBufferExt};
use std::time::Instant;
pub struct Force(pub bool);
......@@ -63,6 +66,10 @@ impl AppOp {
} else if self.rooms.contains_key(&room.id) {
// TODO: update the existing rooms
let update_room = self.rooms.get_mut(&room.id).unwrap();
if room.language.is_some() {
update_room.language = room.language.clone();
};
let typing_users: Vec<Member> = room
.typing_users
.iter()
......@@ -152,6 +159,9 @@ impl AppOp {
pub fn set_active_room_by_id(&mut self, id: String) {
let access_token = unwrap_or_unit_return!(self.access_token.clone());
if let Some(room) = self.rooms.get(&id) {
if let Some(language) = room.language.clone() {
self.set_language(language);
}
if let RoomMembership::Invited(ref sender) = room.membership {
self.show_inv_dialog(Some(sender), room.name.as_ref());
self.invitation_roomid = Some(room.id.clone());
......@@ -698,4 +708,17 @@ impl AppOp {
.unwrap();
}
}
pub fn set_language(&self, lang_code: String) {
if let Some(language) = &gspell::Language::lookup(&lang_code) {
let textview = self.ui.sventry.view.upcast_ref::<gtk::TextView>();
if let Some(gs_checker) = textview
.get_buffer()
.and_then(|gtk_buffer| TextBuffer::get_from_gtk_text_buffer(&gtk_buffer))
.and_then(|gs_buffer| GspellTextBufferExt::get_spell_checker(&gs_buffer))
{
CheckerExt::set_language(&gs_checker, Some(language))
}
}
}
}
......@@ -308,6 +308,10 @@ impl Backend {
let r = room::invite(self, server, access_token, room, userid);
bkerror!(r, tx, BKResponse::InviteError);
}
Ok(BKCommand::ChangeLanguage(access_token, server, lang, room)) => {
let r = room::set_language(self, access_token, server, &room, &lang);
bkerror2!(r, tx, BKResponse::ChangeLanguage);
}
// Media module
Ok(BKCommand::GetThumbAsync(server, media, ctx)) => {
......
......@@ -30,6 +30,7 @@ use crate::backend::types::BackendData;
use crate::backend::types::RoomType;
use crate::r0::filter::RoomEventFilter;
use crate::r0::sync::sync_events::Language;
use crate::r0::AccessToken;
use crate::types::ExtraContent;
use crate::types::Member;
......@@ -954,3 +955,34 @@ fn put_media(url: &str, file: Vec<u8>) -> Result<JsonValue, Error> {
.json()
.or(Err(Error::BackendError))
}
pub fn set_language(
bk: &Backend,
access_token: AccessToken,
server: Url,
roomid: &str,
language_code: &str,
) -> Result<(), Error> {
let userid = bk.data.lock().unwrap().user_id.clone();
let url = bk.url(
server,
&access_token,
&format!(
"user/{}/rooms/{}/account_data/org.gnome.fractal.language",
userid,
roomid.clone()
),
vec![],
)?;
let body = json!(Language {
input_language: language_code.to_string(),
});
put!(url, &body, |_| {}, |err| {
error!(
"Matrix failed to set room language with error code: {:?}",
err
)
});
Ok(())
}
......@@ -86,6 +86,7 @@ pub enum BKCommand {
ListStickers(AccessToken),
SendSticker(Url, AccessToken, String, Sticker),
PurchaseSticker(AccessToken, StickerGroup),
ChangeLanguage(AccessToken, Url, String, String),
}
#[derive(Debug)]
......@@ -144,6 +145,7 @@ pub enum BKResponse {
SetRoomError(Error),
GetFileAsyncError(Error),
InviteError(Error),
ChangeLanguage(Result<(), Error>),
}
#[derive(Debug, Clone, Copy)]
......
......@@ -94,6 +94,7 @@ pub struct Room {
pub direct: bool,
pub prev_batch: Option<String>,
pub typing_users: Vec<Member>,
pub language: Option<String>,
/// Hashmap with the room users power levels
/// the key will be the userid and the value will be the level
......@@ -131,6 +132,11 @@ impl Room {
.find_map(|tag| tag["content"]["tags"]["m.favourite"].as_object())
.and(Some(RoomTag::Favourite))
.unwrap_or(RoomTag::None);
let room_lang = dataevs
.iter()
.filter(|x| x["type"] == "org.gnome.fractal.language")
.find_map(|entry| entry["content"]["input_language"].as_str())
.map(|lang| lang.to_string());
let mut r = Self {
name: calculate_room_name(stevents, userid),
......@@ -150,6 +156,7 @@ impl Room {
.filter_map(parse_room_member)
.map(|m| (m.uid.clone(), m))
.collect(),
language: room_lang,
..Self::new(k.clone(), RoomMembership::Joined(room_tag))
};
......
......@@ -173,6 +173,11 @@ pub struct AccountData {
pub events: Vec<JsonValue>,
}
#[derive(Clone, Debug, Serialize)]
pub struct Language {
pub input_language: String,
}
#[derive(Clone, Debug, Deserialize)]
pub struct ToDevice {
// TODO: Implement Event
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment