diff --git a/crates/matrix-sdk/src/account.rs b/crates/matrix-sdk/src/account.rs new file mode 100644 index 000000000..785bc8bdf --- /dev/null +++ b/crates/matrix-sdk/src/account.rs @@ -0,0 +1,181 @@ +use std::io::Read; + +use matrix_sdk_base::media::{MediaFormat, MediaRequest, MediaType}; +use mime::Mime; +use ruma::{ + api::client::r0::profile::{ + get_avatar_url, get_display_name, set_avatar_url, set_display_name, + }, + MxcUri, +}; + +use crate::{config::RequestConfig, Client, Error, Result}; + +/// A high-level API to manage the client owner's account. +/// +/// All the methods on this struct send a request to the homeserver. +#[derive(Debug, Clone)] +pub struct Account { + /// The underlying HTTP client. + client: Client, +} + +impl Account { + pub(crate) fn new(client: Client) -> Self { + Self { client } + } + + /// Get the display name of the account. + /// + /// # Example + /// ```no_run + /// # use futures::executor::block_on; + /// # use matrix_sdk::Client; + /// # use url::Url; + /// # let homeserver = Url::parse("http://example.com").unwrap(); + /// # block_on(async { + /// let user = "example"; + /// let client = Client::new(homeserver).await.unwrap(); + /// client.login(user, "password", None, None).await.unwrap(); + /// + /// if let Some(name) = client.account().get_display_name().await.unwrap() { + /// println!("Logged in as user '{}' with display name '{}'", user, name); + /// } + /// # }) + /// ``` + pub async fn get_display_name(&self) -> Result> { + let user_id = self.client.user_id().await.ok_or(Error::AuthenticationRequired)?; + let request = get_display_name::Request::new(&user_id); + let response = self.client.send(request, None).await?; + Ok(response.displayname) + } + + /// Set the display name of the account. + /// + /// # Example + /// ```no_run + /// # use futures::executor::block_on; + /// # use matrix_sdk::Client; + /// # use url::Url; + /// # let homeserver = Url::parse("http://example.com").unwrap(); + /// # block_on(async { + /// let user = "example"; + /// let client = Client::new(homeserver).await.unwrap(); + /// client.login(user, "password", None, None).await.unwrap(); + /// + /// client.account().set_display_name(Some("Alice")).await.expect("Failed setting display name"); + /// # }) + /// ``` + pub async fn set_display_name(&self, name: Option<&str>) -> Result<()> { + let user_id = self.client.user_id().await.ok_or(Error::AuthenticationRequired)?; + let request = set_display_name::Request::new(&user_id, name); + self.client.send(request, None).await?; + Ok(()) + } + + /// Get the MXC avatar url of the account, if set. + /// + /// # Example + /// ```no_run + /// # use futures::executor::block_on; + /// # use matrix_sdk::Client; + /// # use url::Url; + /// # let homeserver = Url::parse("http://example.com").unwrap(); + /// # block_on(async { + /// # let user = "example"; + /// let client = Client::new(homeserver).await.unwrap(); + /// client.login(user, "password", None, None).await.unwrap(); + /// + /// if let Some(url) = client.account().get_avatar_url().await.unwrap() { + /// println!("Your avatar's mxc url is {}", url); + /// } + /// # }) + /// ``` + pub async fn get_avatar_url(&self) -> Result>> { + let user_id = self.client.user_id().await.ok_or(Error::AuthenticationRequired)?; + let request = get_avatar_url::Request::new(&user_id); + + let config = Some(RequestConfig::new().force_auth()); + + let response = self.client.send(request, config).await?; + Ok(response.avatar_url) + } + + /// Set the MXC avatar url of the account. + /// + /// The avatar is unset if `url` is `None`. + pub async fn set_avatar_url(&self, url: Option<&MxcUri>) -> Result<()> { + let user_id = self.client.user_id().await.ok_or(Error::AuthenticationRequired)?; + let request = set_avatar_url::Request::new(&user_id, url); + self.client.send(request, None).await?; + Ok(()) + } + + /// Get the account's avatar, if set. + /// + /// Returns the avatar. + /// + /// If a thumbnail is requested no guarantee on the size of the image is + /// given. + /// + /// # Arguments + /// + /// * `format` - The desired format of the avatar. + /// + /// # Example + /// ```no_run + /// # use futures::executor::block_on; + /// # use matrix_sdk::Client; + /// # use matrix_sdk::ruma::room_id; + /// # use matrix_sdk::media::MediaFormat; + /// # use url::Url; + /// # let homeserver = Url::parse("http://example.com").unwrap(); + /// # block_on(async { + /// # let user = "example"; + /// let client = Client::new(homeserver).await.unwrap(); + /// client.login(user, "password", None, None).await.unwrap(); + /// + /// if let Some(avatar) = client.account().get_avatar(MediaFormat::File).await.unwrap() { + /// std::fs::write("avatar.png", avatar); + /// } + /// # }) + /// ``` + pub async fn get_avatar(&self, format: MediaFormat) -> Result>> { + if let Some(url) = self.get_avatar_url().await? { + let request = MediaRequest { media_type: MediaType::Uri(url), format }; + Ok(Some(self.client.get_media_content(&request, true).await?)) + } else { + Ok(None) + } + } + + /// Upload and set the account's avatar. + /// + /// This will upload the data produced by the reader to the homeserver's + /// content repository, and set the user's avatar to the MXC url for the + /// uploaded file. + /// + /// This is a convenience method for calling [`Client::upload()`], + /// followed by [`set_avatar_url()`](#method.set_avatar_url). + /// + /// # Example + /// ```no_run + /// # use std::{path::Path, fs::File, io::Read}; + /// # use futures::executor::block_on; + /// # use matrix_sdk::Client; + /// # use url::Url; + /// # block_on(async { + /// # let homeserver = Url::parse("http://localhost:8080").unwrap(); + /// # let client = Client::new(homeserver).await.unwrap(); + /// let path = Path::new("/home/example/selfie.jpg"); + /// let mut image = File::open(&path).unwrap(); + /// + /// client.account().upload_avatar(&mime::IMAGE_JPEG, &mut image).await.expect("Can't set avatar"); + /// # }) + /// ``` + pub async fn upload_avatar(&self, content_type: &Mime, reader: &mut R) -> Result<()> { + let upload_response = self.client.upload(content_type, reader).await?; + self.set_avatar_url(Some(&upload_response.content_uri)).await?; + Ok(()) + } +} diff --git a/crates/matrix-sdk/src/client.rs b/crates/matrix-sdk/src/client.rs index 5493e9e9a..f7e6dcdb8 100644 --- a/crates/matrix-sdk/src/client.rs +++ b/crates/matrix-sdk/src/client.rs @@ -48,7 +48,6 @@ use ruma::{ filter::{create_filter::Request as FilterUploadRequest, FilterDefinition}, media::{create_content, get_content, get_content_thumbnail}, membership::{join_room_by_id, join_room_by_id_or_alias}, - profile::{get_avatar_url, get_display_name, set_avatar_url, set_display_name}, push::get_notifications::Notification, room::create_room, session::{get_login_types, login, sso_login}, @@ -73,7 +72,7 @@ use crate::{ error::{HttpError, HttpResult}, event_handler::{EventHandler, EventHandlerData, EventHandlerResult, EventKind, SyncEvent}, http_client::{client_with_config, HttpClient}, - room, Error, Result, + room, Account, Error, Result, }; /// A conservative upload speed of 1Mbps @@ -398,161 +397,14 @@ impl Client { self.inner.base_client.session().read().await.clone() } - /// Fetches the display name of the owner of the client. - /// - /// # Example - /// ```no_run - /// # use futures::executor::block_on; - /// # use matrix_sdk::Client; - /// # use url::Url; - /// # let homeserver = Url::parse("http://example.com").unwrap(); - /// # block_on(async { - /// let user = "example"; - /// let client = Client::new(homeserver).await.unwrap(); - /// client.login(user, "password", None, None).await.unwrap(); - /// - /// if let Some(name) = client.display_name().await.unwrap() { - /// println!("Logged in as user '{}' with display name '{}'", user, name); - /// } - /// # }) - /// ``` - pub async fn display_name(&self) -> Result> { - let user_id = self.user_id().await.ok_or(Error::AuthenticationRequired)?; - let request = get_display_name::Request::new(&user_id); - let response = self.send(request, None).await?; - Ok(response.displayname) - } - - /// Sets the display name of the owner of the client. - /// - /// # Example - /// ```no_run - /// # use futures::executor::block_on; - /// # use matrix_sdk::Client; - /// # use url::Url; - /// # let homeserver = Url::parse("http://example.com").unwrap(); - /// # block_on(async { - /// let user = "example"; - /// let client = Client::new(homeserver).await.unwrap(); - /// client.login(user, "password", None, None).await.unwrap(); - /// - /// client.set_display_name(Some("Alice")).await.expect("Failed setting display name"); - /// # }) - /// ``` - pub async fn set_display_name(&self, name: Option<&str>) -> Result<()> { - let user_id = self.user_id().await.ok_or(Error::AuthenticationRequired)?; - let request = set_display_name::Request::new(&user_id, name); - self.send(request, None).await?; - Ok(()) - } - - /// Gets the mxc avatar url of the owner of the client, if set. - /// - /// # Example - /// ```no_run - /// # use futures::executor::block_on; - /// # use matrix_sdk::Client; - /// # use url::Url; - /// # let homeserver = Url::parse("http://example.com").unwrap(); - /// # block_on(async { - /// # let user = "example"; - /// let client = Client::new(homeserver).await.unwrap(); - /// client.login(user, "password", None, None).await.unwrap(); - /// - /// if let Some(url) = client.avatar_url().await.unwrap() { - /// println!("Your avatar's mxc url is {}", url); - /// } - /// # }) - /// ``` - pub async fn avatar_url(&self) -> Result>> { - let user_id = self.user_id().await.ok_or(Error::AuthenticationRequired)?; - let request = get_avatar_url::Request::new(&user_id); - - let config = Some(RequestConfig::new().force_auth()); - - let response = self.send(request, config).await?; - Ok(response.avatar_url) - } - - /// Gets the avatar of the owner of the client, if set. - /// - /// Returns the avatar. - /// If a thumbnail is requested no guarantee on the size of the image is - /// given. - /// - /// # Arguments - /// - /// * `format` - The desired format of the avatar. - /// - /// # Example - /// ```no_run - /// # use futures::executor::block_on; - /// # use matrix_sdk::Client; - /// # use matrix_sdk::ruma::room_id; - /// # use matrix_sdk::media::MediaFormat; - /// # use url::Url; - /// # let homeserver = Url::parse("http://example.com").unwrap(); - /// # block_on(async { - /// # let user = "example"; - /// let client = Client::new(homeserver).await.unwrap(); - /// client.login(user, "password", None, None).await.unwrap(); - /// - /// if let Some(avatar) = client.avatar(MediaFormat::File).await.unwrap() { - /// std::fs::write("avatar.png", avatar); - /// } - /// # }) - /// ``` - pub async fn avatar(&self, format: MediaFormat) -> Result>> { - if let Some(url) = self.avatar_url().await? { - let request = MediaRequest { media_type: MediaType::Uri(url), format }; - Ok(Some(self.get_media_content(&request, true).await?)) - } else { - Ok(None) - } - } - /// Get a reference to the store. pub fn store(&self) -> &Store { self.inner.base_client.store() } - /// Sets the mxc avatar url of the client's owner. The avatar gets unset if - /// `url` is `None`. - pub async fn set_avatar_url(&self, url: Option<&MxcUri>) -> Result<()> { - let user_id = self.user_id().await.ok_or(Error::AuthenticationRequired)?; - let request = set_avatar_url::Request::new(&user_id, url); - self.send(request, None).await?; - Ok(()) - } - - /// Upload and set the owning client's avatar. - /// - /// The will upload the data produced by the reader to the homeserver's - /// content repository, and set the user's avatar to the mxc url for the - /// uploaded file. - /// - /// This is a convenience method for calling [`upload()`](#method.upload), - /// followed by [`set_avatar_url()`](#method.set_avatar_url). - /// - /// # Example - /// ```no_run - /// # use std::{path::Path, fs::File, io::Read}; - /// # use futures::executor::block_on; - /// # use matrix_sdk::Client; - /// # use url::Url; - /// # block_on(async { - /// # let homeserver = Url::parse("http://localhost:8080").unwrap(); - /// # let client = Client::new(homeserver).await.unwrap(); - /// let path = Path::new("/home/example/selfie.jpg"); - /// let mut image = File::open(&path).unwrap(); - /// - /// client.upload_avatar(&mime::IMAGE_JPEG, &mut image).await.expect("Can't set avatar"); - /// # }) - /// ``` - pub async fn upload_avatar(&self, content_type: &Mime, reader: &mut R) -> Result<()> { - let upload_response = self.upload(content_type, reader).await?; - self.set_avatar_url(Some(&upload_response.content_uri)).await?; - Ok(()) + /// Get the account of the current owner of the client. + pub fn account(&self) -> Account { + Account::new(self.clone()) } /// Register a handler for a specific event type. diff --git a/crates/matrix-sdk/src/lib.rs b/crates/matrix-sdk/src/lib.rs index 552bd11b2..669d01e8e 100644 --- a/crates/matrix-sdk/src/lib.rs +++ b/crates/matrix-sdk/src/lib.rs @@ -46,6 +46,7 @@ pub use reqwest; #[doc(no_inline)] pub use ruma; +mod account; mod client; pub mod config; mod error; @@ -59,6 +60,7 @@ mod sync; #[cfg(feature = "encryption")] pub mod encryption; +pub use account::Account; pub use client::{Client, LoopCtrl}; pub use error::{Error, HttpError, HttpResult, Result}; pub use http_client::HttpSend;