[fractal] Save language chosen for spell check for each room



commit d78b52a3711e52e0681788c207bc6d7b446afee8
Author: sonjita <sonjaleaheinze gmail com>
Date:   Tue Nov 5 13:24:31 2019 +0100

    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.

 fractal-gtk/src/app/backend_loop.rs           |  3 ++
 fractal-gtk/src/app/connect/language.rs       | 40 +++++++++++++++++++++++++++
 fractal-gtk/src/app/connect/mod.rs            |  2 ++
 fractal-gtk/src/appop/mod.rs                  |  2 +-
 fractal-gtk/src/appop/room.rs                 | 23 +++++++++++++++
 fractal-matrix-api/src/backend/mod.rs         |  4 +++
 fractal-matrix-api/src/backend/room.rs        | 32 +++++++++++++++++++++
 fractal-matrix-api/src/backend/types.rs       |  2 ++
 fractal-matrix-api/src/model/room.rs          |  7 +++++
 fractal-matrix-api/src/r0/sync/sync_events.rs |  5 ++++
 10 files changed, 119 insertions(+), 1 deletion(-)
---
diff --git a/fractal-gtk/src/app/backend_loop.rs b/fractal-gtk/src/app/backend_loop.rs
index dd0bc2d7..dcc87d5c 100644
--- a/fractal-gtk/src/app/backend_loop.rs
+++ b/fractal-gtk/src/app/backend_loop.rs
@@ -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;
diff --git a/fractal-gtk/src/app/connect/language.rs b/fractal-gtk/src/app/connect/language.rs
new file mode 100644
index 00000000..86889576
--- /dev/null
+++ b/fractal-gtk/src/app/connect/language.rs
@@ -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();
+                        }
+                    }
+                }
+            });
+        }
+    }
+}
diff --git a/fractal-gtk/src/app/connect/mod.rs b/fractal-gtk/src/app/connect/mod.rs
index 037eb15a..a180abed 100644
--- a/fractal-gtk/src/app/connect/mod.rs
+++ b/fractal-gtk/src/app/connect/mod.rs
@@ -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();
diff --git a/fractal-gtk/src/appop/mod.rs b/fractal-gtk/src/appop/mod.rs
index c2ae85cf..ffd5abc6 100644
--- a/fractal-gtk/src/appop/mod.rs
+++ b/fractal-gtk/src/appop/mod.rs
@@ -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;
diff --git a/fractal-gtk/src/appop/room.rs b/fractal-gtk/src/appop/room.rs
index 19b61698..f2ca6d13 100644
--- a/fractal-gtk/src/appop/room.rs
+++ b/fractal-gtk/src/appop/room.rs
@@ -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))
+            }
+        }
+    }
 }
diff --git a/fractal-matrix-api/src/backend/mod.rs b/fractal-matrix-api/src/backend/mod.rs
index 2e470fe3..6ac048b8 100644
--- a/fractal-matrix-api/src/backend/mod.rs
+++ b/fractal-matrix-api/src/backend/mod.rs
@@ -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)) => {
diff --git a/fractal-matrix-api/src/backend/room.rs b/fractal-matrix-api/src/backend/room.rs
index a38b9926..e3568300 100644
--- a/fractal-matrix-api/src/backend/room.rs
+++ b/fractal-matrix-api/src/backend/room.rs
@@ -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(())
+}
diff --git a/fractal-matrix-api/src/backend/types.rs b/fractal-matrix-api/src/backend/types.rs
index 34a8909d..928381cf 100644
--- a/fractal-matrix-api/src/backend/types.rs
+++ b/fractal-matrix-api/src/backend/types.rs
@@ -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)]
diff --git a/fractal-matrix-api/src/model/room.rs b/fractal-matrix-api/src/model/room.rs
index 027ad176..7186d5a6 100644
--- a/fractal-matrix-api/src/model/room.rs
+++ b/fractal-matrix-api/src/model/room.rs
@@ -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))
             };
 
diff --git a/fractal-matrix-api/src/r0/sync/sync_events.rs b/fractal-matrix-api/src/r0/sync/sync_events.rs
index e23c3298..95bae1ac 100644
--- a/fractal-matrix-api/src/r0/sync/sync_events.rs
+++ b/fractal-matrix-api/src/r0/sync/sync_events.rs
@@ -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


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]