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.
This commit is contained in:
sonjita 2019-11-05 13:24:31 +01:00 committed by Daniel Garcia Moreno
parent 1dac3f21d4
commit d78b52a371
10 changed files with 119 additions and 1 deletions

View file

@ -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("Cant login, try again");
let st = AppState::Login;

View file

@ -0,0 +1,40 @@
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();
}
}
}
});
}
}
}

View file

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

View file

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

View file

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

View file

@ -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)) => {

View file

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

View file

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

View file

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

View file

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