[fractal] Send room messages and set room avatar through matrix-sdk
- From: Alexandre Franke <afranke src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [fractal] Send room messages and set room avatar through matrix-sdk
- Date: Tue, 1 Dec 2020 20:22:00 +0000 (UTC)
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: >k::Window,
- server_url: Url,
- access_token: AccessToken,
-) -> gio::SimpleActionGroup {
+pub fn new(window: >k::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, ¶ms, &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, ¶ms, &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, ¶ms_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: >k::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]