[fractal] Send room messages and set room avatar through matrix-sdk



commit ce3c5962e8c41ff92029784ff792a26150f76dad
Author: Alejandro DomĂ­nguez <adomu net-c com>
Date:   Thu Aug 20 05:13:14 2020 +0200

    Send room messages and set room avatar through matrix-sdk

 fractal-gtk/src/actions/room_settings.rs           |  41 ++---
 fractal-gtk/src/appop/message.rs                   |  56 +++----
 fractal-gtk/src/appop/room_settings.rs             |   2 +-
 fractal-gtk/src/backend/room.rs                    | 167 +++++++++++----------
 fractal-gtk/src/model/message.rs                   |   1 +
 fractal-gtk/src/widgets/room_settings.rs           |   5 +-
 fractal-matrix-api/src/meson.build                 |   2 -
 fractal-matrix-api/src/r0.rs                       |   1 -
 fractal-matrix-api/src/r0/message.rs               |   1 -
 .../src/r0/message/create_message_event.rs         |  36 -----
 10 files changed, 133 insertions(+), 179 deletions(-)
---
diff --git a/fractal-gtk/src/actions/room_settings.rs b/fractal-gtk/src/actions/room_settings.rs
index 10c22337..5bac500c 100644
--- a/fractal-gtk/src/actions/room_settings.rs
+++ b/fractal-gtk/src/actions/room_settings.rs
@@ -1,15 +1,13 @@
 use crate::backend::room;
 use fractal_api::identifiers::RoomId;
-use fractal_api::r0::AccessToken;
-use fractal_api::url::Url;
+use fractal_api::Client as MatrixClient;
 use gio::prelude::*;
 use gio::SimpleAction;
 use gio::SimpleActionGroup;
 use glib::clone;
 use std::convert::TryFrom;
-use std::thread;
 
-use crate::app::App;
+use crate::app::{App, RUNTIME};
 use crate::backend::HandleError;
 use crate::util::i18n::i18n;
 
@@ -19,11 +17,7 @@ use crate::widgets::FileDialog::open;
 use crate::actions::ButtonState;
 
 // This creates all actions a user can perform in the room settings
-pub fn new(
-    window: &gtk::Window,
-    server_url: Url,
-    access_token: AccessToken,
-) -> gio::SimpleActionGroup {
+pub fn new(window: &gtk::Window, session_client: MatrixClient) -> gio::SimpleActionGroup {
     let actions = SimpleActionGroup::new();
     // TODO create two stats loading interaction and connect it to the avatar box
     let change_avatar = SimpleAction::new_stateful(
@@ -42,24 +36,21 @@ pub fn new(
             let filter = gtk::FileFilter::new();
             filter.set_name(Some(i18n("Images").as_str()));
             filter.add_mime_type("image/*");
-            if let Some(path) = open(&window, i18n("Select a new avatar").as_str(), &[filter]) {
-                if let Some(file) = path.to_str().map(Into::into) {
-                    a.change_state(&ButtonState::Insensitive.into());
-                    let server = server_url.clone();
-                    let access_token = access_token.clone();
-                    thread::spawn(move || {
-                        match room::set_room_avatar(server, access_token, room_id, file) {
-                            Ok(_) => {
-                                APPOP!(show_new_room_avatar);
-                            }
-                            Err(err) => {
-                                err.handle_error();
-                            }
+            if let Some(file) = open(&window, i18n("Select a new avatar").as_str(), &[filter]) {
+                a.change_state(&ButtonState::Insensitive.into());
+                let session_client = session_client.clone();
+                RUNTIME.spawn(async move {
+                    match room::set_room_avatar(session_client, &room_id, &file).await {
+                        Ok(_) => {
+                            APPOP!(show_new_room_avatar);
                         }
-                    });
-                } else {
+                        Err(err) => {
+                            err.handle_error();
+                        }
+                    }
+                });
+            } else {
                     ErrorDialog::new(false, &i18n("Couldn’t open file"));
-                }
             }
         }
     }));
diff --git a/fractal-gtk/src/appop/message.rs b/fractal-gtk/src/appop/message.rs
index 4f1ab7aa..a8623257 100644
--- a/fractal-gtk/src/appop/message.rs
+++ b/fractal-gtk/src/appop/message.rs
@@ -2,11 +2,10 @@ use crate::backend::{room, HandleError};
 use crate::model::fileinfo::ExtraContent;
 use comrak::{markdown_to_html, ComrakOptions};
 use fractal_api::identifiers::{EventId, RoomId};
-use fractal_api::r0::AccessToken;
 use fractal_api::url::Url;
+use fractal_api::Client as MatrixClient;
 use gdk_pixbuf::Pixbuf;
 use gio::prelude::FileExt;
-use glib::clone;
 use glib::source::Continue;
 use gtk::prelude::*;
 use lazy_static::lazy_static;
@@ -17,7 +16,6 @@ use serde_json::Value as JsonValue;
 use std::env::temp_dir;
 use std::fs;
 use std::path::{Path, PathBuf};
-use std::thread;
 
 use crate::app::RUNTIME;
 use crate::appop::room::Force;
@@ -189,14 +187,14 @@ impl AppOp {
         None
     }
 
-    pub fn msg_sent(&mut self, _txid: String, evid: Option<EventId>) -> Option<()> {
+    pub fn msg_sent(&mut self, evid: EventId) -> Option<()> {
         let messages = self.history.as_ref()?.get_listbox();
         if let Some(ref mut m) = self.msg_queue.pop() {
             if let Some(ref w) = m.widget {
                 messages.remove(w);
             }
             m.widget = None;
-            m.msg.id = evid;
+            m.msg.id = Some(evid);
             self.show_room_messages(vec![m.msg.clone()]);
         }
         self.force_dequeue_message();
@@ -217,7 +215,7 @@ impl AppOp {
     }
 
     pub fn dequeue_message(&mut self) -> Option<()> {
-        let login_data = self.login_data.clone()?;
+        let session_client = self.login_data.as_ref()?.session_client.clone();
         if self.sending_message {
             return None;
         }
@@ -225,25 +223,15 @@ impl AppOp {
         self.sending_message = true;
         if let Some(next) = self.msg_queue.last() {
             let msg = next.msg.clone();
-            match &next.msg.mtype[..] {
+            match next.msg.mtype.as_str() {
                 "m.image" | "m.file" | "m.audio" | "m.video" => {
-                    thread::spawn(move || {
-                        attach_file(
-                            login_data.session_client.homeserver().clone(),
-                            login_data.access_token,
-                            msg,
-                        )
-                    });
+                    RUNTIME.spawn(attach_file(session_client, msg));
                 }
                 _ => {
-                    thread::spawn(move || {
-                        match room::send_msg(
-                            login_data.session_client.homeserver().clone(),
-                            login_data.access_token,
-                            msg,
-                        ) {
-                            Ok((txid, evid)) => {
-                                APPOP!(msg_sent, (txid, evid));
+                    RUNTIME.spawn(async move {
+                        match room::send_msg(session_client, msg).await {
+                            Ok(evid) => {
+                                APPOP!(msg_sent, (evid));
                                 let initial = false;
                                 let number_tries = 0;
                                 APPOP!(sync, (initial, number_tries));
@@ -675,7 +663,7 @@ fn get_file_media_info(file: &Path, mimetype: &str) -> Option<JsonValue> {
 
 struct NonMediaMsg;
 
-fn attach_file(baseu: Url, tk: AccessToken, mut msg: Message) -> Result<(), NonMediaMsg> {
+async fn attach_file(session_client: MatrixClient, mut msg: Message) -> Result<(), NonMediaMsg> {
     let mut extra_content: Option<ExtraContent> = msg
         .extra_content
         .clone()
@@ -685,13 +673,14 @@ fn attach_file(baseu: Url, tk: AccessToken, mut msg: Message) -> Result<(), NonM
 
     match (msg.url.clone(), msg.local_path.as_ref(), thumb_url) {
         (Some(url), _, Some(thumb)) if url.scheme() == "mxc" && thumb.scheme() == "mxc" => {
-            send_msg_and_manage(baseu, tk, msg);
+            send_msg_and_manage(session_client, msg).await;
 
             Ok(())
         }
         (_, Some(local_path), _) => {
             if let Some(ref local_path_thumb) = msg.local_path_thumb {
-                let response = room::upload_file(baseu.clone(), tk.clone(), local_path_thumb)
+                let response = room::upload_file(session_client.clone(), local_path_thumb)
+                    .await
                     .and_then(|response| Url::parse(&response.content_uri).map_err(Into::into));
 
                 match response {
@@ -712,12 +701,11 @@ fn attach_file(baseu: Url, tk: AccessToken, mut msg: Message) -> Result<(), NonM
                 }
             }
 
-            let query =
-                room::upload_file(baseu.clone(), tk.clone(), local_path).and_then(|response| {
+            let query = room::upload_file(session_client.clone(), &local_path)
+                .await
+                .and_then(|response| {
                     msg.url = Some(Url::parse(&response.content_uri)?);
-                    thread::spawn(
-                        clone!(@strong msg => move || send_msg_and_manage(baseu, tk, msg)),
-                    );
+                    RUNTIME.spawn(send_msg_and_manage(session_client, msg.clone()));
 
                     Ok(msg)
                 });
@@ -737,10 +725,10 @@ fn attach_file(baseu: Url, tk: AccessToken, mut msg: Message) -> Result<(), NonM
     }
 }
 
-fn send_msg_and_manage(baseu: Url, tk: AccessToken, msg: Message) {
-    match room::send_msg(baseu, tk, msg) {
-        Ok((txid, evid)) => {
-            APPOP!(msg_sent, (txid, evid));
+async fn send_msg_and_manage(session_client: MatrixClient, msg: Message) {
+    match room::send_msg(session_client, msg).await {
+        Ok(evid) => {
+            APPOP!(msg_sent, (evid));
             let initial = false;
             let number_tries = 0;
             APPOP!(sync, (initial, number_tries));
diff --git a/fractal-gtk/src/appop/room_settings.rs b/fractal-gtk/src/appop/room_settings.rs
index dfcae863..4b939823 100644
--- a/fractal-gtk/src/appop/room_settings.rs
+++ b/fractal-gtk/src/appop/room_settings.rs
@@ -22,10 +22,10 @@ impl AppOp {
         {
             let room = self.rooms.get(&self.active_room.clone()?)?;
             let mut panel = widgets::RoomSettings::new(
+                login_data.session_client.clone(),
                 &window,
                 login_data.uid,
                 room.clone(),
-                login_data.session_client.homeserver().clone(),
                 login_data.access_token,
             );
             let page = panel.create(login_data.session_client.clone())?;
diff --git a/fractal-gtk/src/backend/room.rs b/fractal-gtk/src/backend/room.rs
index 17f4c4bd..33eff4a6 100644
--- a/fractal-gtk/src/backend/room.rs
+++ b/fractal-gtk/src/backend/room.rs
@@ -1,16 +1,15 @@
 use log::error;
 use serde_json::json;
 
-use fractal_api::reqwest::Error as ReqwestError;
-use fractal_api::url::{ParseError as UrlError, Url};
 use fractal_api::{
     identifiers::{Error as IdError, EventId, RoomId, RoomIdOrAliasId, UserId},
+    reqwest::Error as ReqwestError,
+    url::{ParseError as UrlError, Url},
     Client as MatrixClient, Error as MatrixError, FromHttpResponseError as RumaResponseError,
     ServerError,
 };
-use std::fs;
 use std::io::Error as IoError;
-use std::path::{Path, PathBuf};
+use std::path::Path;
 
 use std::convert::TryFrom;
 use std::time::Duration;
@@ -30,26 +29,28 @@ use crate::model::{
 use fractal_api::api::r0::config::get_global_account_data::Request as GetGlobalAccountDataRequest;
 use fractal_api::api::r0::config::set_global_account_data::Request as SetGlobalAccountDataRequest;
 use fractal_api::api::r0::filter::RoomEventFilter;
+use fractal_api::api::r0::media::create_content::Request as CreateContentRequest;
+use fractal_api::api::r0::media::create_content::Response as CreateContentResponse;
 use fractal_api::api::r0::message::get_message_events::Request as GetMessagesEventsRequest;
 use fractal_api::api::r0::room::create_room::Request as CreateRoomRequest;
 use fractal_api::api::r0::room::create_room::RoomPreset;
 use fractal_api::api::r0::room::Visibility;
+use fractal_api::api::r0::state::send_state_event_for_key::Request as SendStateEventForKeyRequest;
 use fractal_api::api::r0::typing::create_typing_event::Typing;
 use fractal_api::assign;
+use fractal_api::events::room::avatar::AvatarEventContent;
 use fractal_api::events::room::history_visibility::HistoryVisibility;
 use fractal_api::events::room::history_visibility::HistoryVisibilityEventContent;
+use fractal_api::events::room::message::MessageEventContent;
 use fractal_api::events::AnyBasicEventContent;
 use fractal_api::events::AnyInitialStateEvent;
+use fractal_api::events::AnyMessageEventContent;
+use fractal_api::events::AnyStateEventContent;
+use fractal_api::events::EventContent;
 use fractal_api::events::EventType;
 use fractal_api::events::InitialStateEvent;
 use fractal_api::r0::config::set_room_account_data::request as set_room_account_data;
 use fractal_api::r0::config::set_room_account_data::Parameters as SetRoomAccountDataParameters;
-use fractal_api::r0::media::create_content::request as create_content;
-use fractal_api::r0::media::create_content::Parameters as CreateContentParameters;
-use fractal_api::r0::media::create_content::Response as CreateContentResponse;
-use fractal_api::r0::message::create_message_event::request as create_message_event;
-use fractal_api::r0::message::create_message_event::Parameters as CreateMessageEventParameters;
-use fractal_api::r0::message::create_message_event::Response as CreateMessageEventResponse;
 use fractal_api::r0::pushrules::delete_room_rules::request as delete_room_rules;
 use fractal_api::r0::pushrules::delete_room_rules::Parameters as DelRoomRulesParams;
 use fractal_api::r0::pushrules::get_room_rules::request as get_room_rules;
@@ -76,6 +77,7 @@ use fractal_api::r0::tag::delete_tag::Parameters as DeleteTagParameters;
 use fractal_api::r0::AccessToken;
 
 use serde_json::value::to_raw_value;
+use serde_json::Error as ParseJsonError;
 use serde_json::Value as JsonValue;
 
 use super::{
@@ -263,61 +265,79 @@ pub async fn get_room_messages_from_msg(
 }
 
 #[derive(Debug)]
-pub struct SendMsgError(String);
+pub enum SendMsgError {
+    Matrix(MatrixError),
+    ParseEvent(ParseJsonError),
+}
+
+impl From<MatrixError> for SendMsgError {
+    fn from(err: MatrixError) -> Self {
+        Self::Matrix(err)
+    }
+}
+
+impl From<ParseJsonError> for SendMsgError {
+    fn from(err: ParseJsonError) -> Self {
+        Self::ParseEvent(err)
+    }
+}
 
 impl HandleError for SendMsgError {
     fn handle_error(&self) {
-        error!("sending {}: retrying send", self.0);
-        APPOP!(retry_send);
+        match self {
+            Self::Matrix(matrix_err) => {
+                error!("Failed sending message, retrying send: {}", matrix_err);
+                APPOP!(retry_send);
+            }
+            Self::ParseEvent(parse_err) => {
+                error!(
+                    "Failed constructing the message event for sending. Please report upstream: {:?}",
+                    parse_err
+                );
+            }
+        }
     }
 }
 
-pub fn send_msg(
-    base: Url,
-    access_token: AccessToken,
-    msg: Message,
-) -> Result<(String, Option<EventId>), SendMsgError> {
-    let room_id: RoomId = msg.room.clone();
+pub async fn send_msg(session_client: MatrixClient, msg: Message) -> Result<EventId, SendMsgError> {
+    let room_id: RoomId = msg.room;
 
-    let params = CreateMessageEventParameters { access_token };
-
-    let mut body = json!({
+    let mut event = json!({
         "body": msg.body,
         "msgtype": msg.mtype,
     });
 
     if let Some(u) = msg.url.as_ref() {
-        body["url"] = json!(u);
+        event["url"] = json!(u);
     }
 
     if let (Some(f), Some(f_b)) = (msg.format.as_ref(), msg.formatted_body.as_ref()) {
-        body["formatted_body"] = json!(f_b);
-        body["format"] = json!(f);
+        event["formatted_body"] = json!(f_b);
+        event["format"] = json!(f);
     }
 
     let extra_content_map = msg
         .extra_content
-        .as_ref()
-        .and_then(|v| v.as_object())
-        .cloned()
-        .unwrap_or_default();
+        .into_iter()
+        .filter_map(|v| v.as_object().cloned())
+        .flatten();
 
     for (k, v) in extra_content_map {
-        body[k] = v;
+        event[k] = v;
     }
 
-    let txn_id = msg.get_txn_id();
+    let raw_event = to_raw_value(&event)?;
+    let message_event_content = MessageEventContent::from_parts("m.room.message", raw_event)?;
 
-    create_message_event(base, &params, &body, &room_id, "m.room.message", &txn_id)
-        .and_then(|request| {
-            let response = HTTP_CLIENT
-                .get_client()
-                .execute(request)?
-                .json::<CreateMessageEventResponse>()?;
+    let response = session_client
+        .room_send(
+            &room_id,
+            AnyMessageEventContent::RoomMessage(message_event_content),
+            None,
+        )
+        .await?;
 
-            Ok((txn_id.clone(), response.event_id))
-        })
-        .or(Err(SendMsgError(txn_id)))
+    Ok(response.event_id)
 }
 
 #[derive(Debug)]
@@ -528,13 +548,13 @@ pub fn set_room_topic(
 #[derive(Debug)]
 pub enum SetRoomAvatarError {
     Io(IoError),
-    Reqwest(ReqwestError),
+    Matrix(MatrixError),
     ParseUrl(UrlError),
 }
 
-impl From<ReqwestError> for SetRoomAvatarError {
-    fn from(err: ReqwestError) -> Self {
-        Self::Reqwest(err)
+impl From<MatrixError> for SetRoomAvatarError {
+    fn from(err: MatrixError) -> Self {
+        Self::Matrix(err)
     }
 }
 
@@ -542,7 +562,7 @@ impl From<AttachedFileError> for SetRoomAvatarError {
     fn from(err: AttachedFileError) -> Self {
         match err {
             AttachedFileError::Io(err) => Self::Io(err),
-            AttachedFileError::Reqwest(err) => Self::Reqwest(err),
+            AttachedFileError::Matrix(err) => Self::Matrix(err),
             AttachedFileError::ParseUrl(err) => Self::ParseUrl(err),
         }
     }
@@ -550,21 +570,19 @@ impl From<AttachedFileError> for SetRoomAvatarError {
 
 impl HandleError for SetRoomAvatarError {}
 
-pub fn set_room_avatar(
-    base: Url,
-    access_token: AccessToken,
-    room_id: RoomId,
-    avatar: PathBuf,
+pub async fn set_room_avatar(
+    session_client: MatrixClient,
+    room_id: &RoomId,
+    avatar: &Path,
 ) -> Result<(), SetRoomAvatarError> {
-    let params = CreateStateEventsForKeyParameters {
-        access_token: access_token.clone(),
-    };
-
-    let upload_file_response = upload_file(base.clone(), access_token, &avatar)?;
-
-    let body = json!({ "url": upload_file_response.content_uri.as_str() });
-    let request = create_state_events_for_key(base, &params, &body, &room_id, "m.room.avatar")?;
-    HTTP_CLIENT.get_client().execute(request)?;
+    let avatar_uri = upload_file(session_client.clone(), avatar)
+        .await?
+        .content_uri;
+    let content = &AnyStateEventContent::RoomAvatar(assign!(AvatarEventContent::new(), {
+        url: Some(avatar_uri),
+    }));
+    let request = SendStateEventForKeyRequest::new(room_id, "m.room.avatar", content);
+    session_client.send(request).await?;
 
     Ok(())
 }
@@ -572,13 +590,13 @@ pub fn set_room_avatar(
 #[derive(Debug)]
 pub enum AttachedFileError {
     Io(IoError),
-    Reqwest(ReqwestError),
+    Matrix(MatrixError),
     ParseUrl(UrlError),
 }
 
-impl From<ReqwestError> for AttachedFileError {
-    fn from(err: ReqwestError) -> Self {
-        Self::Reqwest(err)
+impl From<MatrixError> for AttachedFileError {
+    fn from(err: MatrixError) -> Self {
+        Self::Matrix(err)
     }
 }
 
@@ -605,24 +623,19 @@ impl HandleError for AttachedFileError {
     }
 }
 
-pub fn upload_file(
-    base: Url,
-    access_token: AccessToken,
+pub async fn upload_file(
+    session_client: MatrixClient,
     fname: &Path,
 ) -> Result<CreateContentResponse, AttachedFileError> {
-    let params_upload = CreateContentParameters {
-        access_token,
-        filename: None,
-    };
+    let file = tokio::fs::read(fname).await?;
+    let (ref content_type, _) = gio::content_type_guess(None, &file);
 
-    let contents = fs::read(fname)?;
-    let request = create_content(base, &params_upload, contents)?;
+    let request = assign!(CreateContentRequest::new(file), {
+        filename: None,
+        content_type: Some(&content_type),
+    });
 
-    HTTP_CLIENT
-        .get_client()
-        .execute(request)?
-        .json()
-        .map_err(Into::into)
+    session_client.send(request).await.map_err(Into::into)
 }
 
 #[derive(Debug, Clone, Copy)]
diff --git a/fractal-gtk/src/model/message.rs b/fractal-gtk/src/model/message.rs
index 22cc9a5a..783a934e 100644
--- a/fractal-gtk/src/model/message.rs
+++ b/fractal-gtk/src/model/message.rs
@@ -102,6 +102,7 @@ impl Message {
     /// message body and the date.
     ///
     /// 
https://matrix.org/docs/spec/client_server/r0.3.0.html#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid
+    // TODO: Return matrix_sdk::uuid::Uuid
     pub fn get_txn_id(&self) -> String {
         let msg_str = format!("{}{}{}", self.room, self.body, self.date);
         let digest = md5::compute(msg_str.as_bytes());
diff --git a/fractal-gtk/src/widgets/room_settings.rs b/fractal-gtk/src/widgets/room_settings.rs
index 4ff08658..3b5ed560 100644
--- a/fractal-gtk/src/widgets/room_settings.rs
+++ b/fractal-gtk/src/widgets/room_settings.rs
@@ -35,12 +35,13 @@ pub struct RoomSettings {
 
 impl RoomSettings {
     pub fn new(
+        session_client: MatrixClient,
         window: &gtk::Window,
         uid: UserId,
         room: Room,
-        server_url: Url,
         access_token: AccessToken,
     ) -> RoomSettings {
+        let server_url = session_client.homeserver().clone();
         let builder = gtk::Builder::new();
 
         builder
@@ -51,7 +52,7 @@ impl RoomSettings {
             .get_object::<gtk::Stack>("room_settings_stack")
             .expect("Can't find room_settings_stack in ui file.");
 
-        let actions = actions::RoomSettings::new(&window, server_url.clone(), access_token.clone());
+        let actions = actions::RoomSettings::new(&window, session_client);
         stack.insert_action_group("room-settings", Some(&actions));
 
         RoomSettings {
diff --git a/fractal-matrix-api/src/meson.build b/fractal-matrix-api/src/meson.build
index d81333c6..577689b9 100644
--- a/fractal-matrix-api/src/meson.build
+++ b/fractal-matrix-api/src/meson.build
@@ -15,7 +15,6 @@ api_sources = files(
   'r0/contact/request_verification_token_email.rs',
   'r0/contact/request_verification_token_msisdn.rs',
   'r0/media/create_content.rs',
-  'r0/message/create_message_event.rs',
   'r0/profile/get_display_name.rs',
   'r0/profile/get_profile.rs',
   'r0/profile/set_avatar_url.rs',
@@ -35,7 +34,6 @@ api_sources = files(
   'r0/contact.rs',
   'r0/filter.rs',
   'r0/media.rs',
-  'r0/message.rs',
   'r0/profile.rs',
   'r0/redact.rs',
   'r0/search.rs',
diff --git a/fractal-matrix-api/src/r0.rs b/fractal-matrix-api/src/r0.rs
index 0a366ca1..635b07f4 100644
--- a/fractal-matrix-api/src/r0.rs
+++ b/fractal-matrix-api/src/r0.rs
@@ -3,7 +3,6 @@ pub mod config;
 pub mod contact;
 pub mod filter;
 pub mod media;
-pub mod message;
 pub mod profile;
 pub mod pushrules;
 pub mod redact;


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