Skip to content

Commit d665ab2

Browse files
committed
refactor: Make SendMediaUploadRequest a builder, allowing to create a media upload request with a more flexible API. Use SendMediaUploadRequest in Media::upload
1 parent 2e32c25 commit d665ab2

File tree

10 files changed

+151
-96
lines changed

10 files changed

+151
-96
lines changed

bindings/matrix-sdk-ffi/src/client.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ use matrix_sdk::{
3939
},
4040
sliding_sync::Version as SdkSlidingSyncVersion,
4141
store::RoomLoadSettings as SdkRoomLoadSettings,
42-
AuthApi, AuthSession, Client as MatrixClient, SessionChange, SessionTokens,
43-
STATE_STORE_DATABASE_NAME,
42+
AuthApi, AuthSession, Client as MatrixClient, SendMediaUploadRequest, SessionChange,
43+
SessionTokens, STATE_STORE_DATABASE_NAME,
4444
};
4545
use matrix_sdk_common::{stream::StreamExt, SendOutsideWasm, SyncOutsideWasm};
4646
use matrix_sdk_ui::{
@@ -1030,18 +1030,20 @@ impl Client {
10301030
progress_watcher: Option<Box<dyn ProgressWatcher>>,
10311031
) -> Result<String, ClientError> {
10321032
let mime_type: mime::Mime = mime_type.parse().context("Parsing mime type")?;
1033-
let request = self.inner.media().upload(&mime_type, data, filename, None);
1033+
let send_media_request = SendMediaUploadRequest::new((*self.inner).clone(), data)
1034+
.with_content_type(mime_type.essence_str().to_owned())
1035+
.with_filename(filename);
10341036

10351037
if let Some(progress_watcher) = progress_watcher {
1036-
let mut subscriber = request.subscribe_to_send_progress();
1038+
let mut subscriber = send_media_request.subscribe_to_send_progress();
10371039
get_runtime_handle().spawn(async move {
10381040
while let Some(progress) = subscriber.next().await {
10391041
progress_watcher.transmission_progress(progress.into());
10401042
}
10411043
});
10421044
}
10431045

1044-
let response = request.await?;
1046+
let response = self.inner.media().upload(send_media_request).await?;
10451047

10461048
Ok(String::from(response.content_uri))
10471049
}

crates/matrix-sdk/src/account.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ use ruma::{
5555
use serde::Deserialize;
5656
use tracing::error;
5757

58-
use crate::{config::RequestConfig, Client, Error, Result};
58+
use crate::{config::RequestConfig, Client, Error, Result, SendMediaUploadRequest};
5959

6060
/// A high-level API to manage the client owner's account.
6161
///
@@ -264,7 +264,10 @@ impl Account {
264264
///
265265
/// [`Media::upload()`]: crate::Media::upload
266266
pub async fn upload_avatar(&self, content_type: &Mime, data: Vec<u8>) -> Result<OwnedMxcUri> {
267-
let upload_response = self.client.media().upload(content_type, data, None, None).await?;
267+
let send_media_request = SendMediaUploadRequest::new(self.client.clone(), data)
268+
.with_content_type(content_type.essence_str().to_owned());
269+
270+
let upload_response = self.client.media().upload(send_media_request).await?;
268271
self.set_avatar_url(Some(&upload_response.content_uri)).await?;
269272
Ok(upload_response.content_uri)
270273
}

crates/matrix-sdk/src/client/futures.rs

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -156,17 +156,50 @@ where
156156
}
157157
}
158158

159-
/// `IntoFuture` used to send media upload requests. It wraps another
160-
/// [`SendRequest`], checking its size will be accepted by the homeserver before
161-
/// uploading.
159+
/// `IntoFuture` used to send media upload requests. It works as a builder which
160+
/// can create a [`SendRequest`] given several configuration options, checking
161+
/// its size will be accepted by the homeserver before uploading.
162162
#[allow(missing_debug_implementations)]
163163
pub struct SendMediaUploadRequest {
164-
send_request: SendRequest<media::create_content::v3::Request>,
164+
client: Client,
165+
request: media::create_content::v3::Request,
166+
/// The [`RequestConfig`] to use for uploading the media file.
167+
pub request_config: Option<RequestConfig>,
168+
progress_observable: SharedObservable<TransmissionProgress>,
165169
}
166170

167171
impl SendMediaUploadRequest {
168-
pub fn new(request: SendRequest<media::create_content::v3::Request>) -> Self {
169-
Self { send_request: request }
172+
/// Creates a new instance.
173+
pub fn new(client: Client, file: Vec<u8>) -> Self {
174+
Self {
175+
client,
176+
request: media::create_content::v3::Request::new(file),
177+
request_config: Default::default(),
178+
progress_observable: SharedObservable::new(TransmissionProgress::default()),
179+
}
180+
}
181+
182+
/// Returns the data to upload.
183+
pub fn data(&self) -> &Vec<u8> {
184+
&self.request.file
185+
}
186+
187+
/// Sets the content type of the media for the media upload request.
188+
pub fn with_content_type(mut self, content_type: impl Into<String>) -> Self {
189+
self.request.content_type = Some(content_type.into());
190+
self
191+
}
192+
193+
/// Sets the filename for the media upload request.
194+
pub fn with_filename(mut self, filename: Option<impl Into<String>>) -> Self {
195+
self.request.filename = filename.map(Into::into);
196+
self
197+
}
198+
199+
/// Applies the provided [`RequestConfig`] to the future [`SendRequest`].
200+
pub fn with_request_config(mut self, request_config: Option<RequestConfig>) -> Self {
201+
self.request_config = request_config;
202+
self
170203
}
171204

172205
/// Replace the default `SharedObservable` used for tracking upload
@@ -179,14 +212,24 @@ impl SendMediaUploadRequest {
179212
mut self,
180213
send_progress: SharedObservable<TransmissionProgress>,
181214
) -> Self {
182-
self.send_request = self.send_request.with_send_progress_observable(send_progress);
215+
self.progress_observable = send_progress;
183216
self
184217
}
185218

186219
/// Get a subscriber to observe the progress of sending the request
187220
/// body.
188221
pub fn subscribe_to_send_progress(&self) -> Subscriber<TransmissionProgress> {
189-
self.send_request.send_progress.subscribe()
222+
self.progress_observable.subscribe()
223+
}
224+
225+
/// Creates the [`SendRequest`] using the provided parameters.
226+
pub fn build_send_request(self) -> SendRequest<media::create_content::v3::Request> {
227+
SendRequest {
228+
client: self.client,
229+
request: self.request,
230+
config: self.request_config,
231+
send_progress: self.progress_observable,
232+
}
190233
}
191234
}
192235

@@ -195,9 +238,9 @@ impl IntoFuture for SendMediaUploadRequest {
195238
boxed_into_future!();
196239

197240
fn into_future(self) -> Self::IntoFuture {
198-
let request_length = self.send_request.request.file.len();
199-
let client = self.send_request.client.clone();
200-
let send_request = self.send_request;
241+
let send_request = self.build_send_request();
242+
let request_length = send_request.request.file.len();
243+
let client = send_request.client.clone();
201244

202245
Box::pin(async move {
203246
let max_upload_size = client.load_or_fetch_max_upload_size().await?;

crates/matrix-sdk/src/client/mod.rs

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,10 @@ mod builder;
111111
pub(crate) mod caches;
112112
pub(crate) mod futures;
113113

114-
pub use self::builder::{sanitize_server_name, ClientBuildError, ClientBuilder};
114+
pub use self::{
115+
builder::{sanitize_server_name, ClientBuildError, ClientBuilder},
116+
futures::SendMediaUploadRequest,
117+
};
115118

116119
#[cfg(not(target_family = "wasm"))]
117120
type NotificationHandlerFut = Pin<Box<dyn Future<Output = ()> + Send>>;
@@ -2909,7 +2912,6 @@ pub(crate) mod tests {
29092912

29102913
use assert_matches::assert_matches;
29112914
use assert_matches2::assert_let;
2912-
use eyeball::SharedObservable;
29132915
use futures_util::{pin_mut, FutureExt};
29142916
use js_int::{uint, UInt};
29152917
use matrix_sdk_base::{
@@ -2950,10 +2952,9 @@ pub(crate) mod tests {
29502952
use crate::{
29512953
client::{futures::SendMediaUploadRequest, WeakClient},
29522954
config::RequestConfig,
2953-
futures::SendRequest,
29542955
media::MediaError,
29552956
test_utils::{client::MockClientBuilder, mocks::MatrixMockServer},
2956-
Error, TransmissionProgress,
2957+
Error,
29572958
};
29582959

29592960
#[async_test]
@@ -3781,15 +3782,7 @@ pub(crate) mod tests {
37813782
assert_eq!(*client.inner.server_max_upload_size.lock().await.get().unwrap(), uint!(1));
37823783

37833784
let data = vec![1, 2];
3784-
let upload_request =
3785-
ruma::api::client::media::create_content::v3::Request::new(data.clone());
3786-
let request = SendRequest {
3787-
client: client.clone(),
3788-
request: upload_request,
3789-
config: None,
3790-
send_progress: SharedObservable::new(TransmissionProgress::default()),
3791-
};
3792-
let media_request = SendMediaUploadRequest::new(request);
3785+
let media_request = SendMediaUploadRequest::new(client, data.clone());
37933786

37943787
let error = media_request.await.err();
37953788
assert_let!(Some(Error::Media(MediaError::MediaTooLargeToUpload { max, current })) = error);

crates/matrix-sdk/src/encryption/futures.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ use eyeball::{SharedObservable, Subscriber};
2323
use matrix_sdk_common::boxed_into_future;
2424
use ruma::events::room::{EncryptedFile, EncryptedFileInit};
2525

26-
use crate::{config::RequestConfig, Client, Media, Result, TransmissionProgress};
26+
use crate::{
27+
config::RequestConfig, Client, Media, Result, SendMediaUploadRequest, TransmissionProgress,
28+
};
2729

2830
/// Future returned by [`Client::upload_encrypted_file`].
2931
#[allow(missing_debug_implementations)]
@@ -89,11 +91,12 @@ where
8991
let request_config =
9092
request_config.map(|config| config.timeout(Media::reasonable_upload_timeout(&buf)));
9193

92-
let response = client
93-
.media()
94-
.upload(&mime::APPLICATION_OCTET_STREAM, buf, None, request_config)
95-
.with_send_progress_observable(send_progress)
96-
.await?;
94+
let send_media_request = SendMediaUploadRequest::new(self.client.clone(), buf)
95+
.with_content_type(mime::APPLICATION_OCTET_STREAM.essence_str())
96+
.with_request_config(request_config)
97+
.with_send_progress_observable(send_progress);
98+
99+
let response = client.media().upload(send_media_request).await?;
97100

98101
let file: EncryptedFile = {
99102
let keys = encryptor.finish();

crates/matrix-sdk/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ pub use sliding_sync::{
9494
uniffi::setup_scaffolding!();
9595

9696
pub mod live_location_share;
97+
98+
pub use client::SendMediaUploadRequest;
99+
97100
#[cfg(any(test, feature = "testing"))]
98101
pub mod test_utils;
99102

crates/matrix-sdk/src/media.rs

Lines changed: 31 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ use tempfile::{Builder as TempFileBuilder, NamedTempFile, TempDir};
4141
use tokio::{fs::File as TokioFile, io::AsyncWriteExt};
4242

4343
use crate::{
44-
attachment::Thumbnail, client::futures::SendMediaUploadRequest, config::RequestConfig, Client,
45-
Error, Result, TransmissionProgress,
44+
attachment::Thumbnail, client::futures::SendMediaUploadRequest, Client, Error, Result,
45+
TransmissionProgress,
4646
};
4747

4848
/// A conservative upload speed of 1Mbps
@@ -165,57 +165,40 @@ impl Media {
165165
Self { client }
166166
}
167167

168-
/// Upload some media to the server.
169-
///
170-
/// # Arguments
171-
///
172-
/// * `content_type` - The type of the media, this will be used as the
173-
/// content-type header.
174-
///
175-
/// * `data` - Vector of bytes to be uploaded to the server.
176-
///
177-
/// * `request_config` - Optional request configuration for the HTTP client,
178-
/// overriding the default. If not provided, a reasonable timeout value is
179-
/// inferred.
168+
/// Upload some media to the server using the provided
169+
/// [`SendMediaUploadRequest`].
180170
///
181171
/// # Examples
182172
///
183173
/// ```no_run
184174
/// # use std::fs;
185-
/// # use matrix_sdk::{Client, ruma::room_id};
175+
/// # use matrix_sdk::{Client, ruma::room_id, SendMediaUploadRequest};
186176
/// # use url::Url;
187177
/// # use mime;
188178
/// # async {
189179
/// # let homeserver = Url::parse("http://localhost:8080")?;
190180
/// # let mut client = Client::new(homeserver).await?;
191181
/// let image = fs::read("/home/example/my-cat.jpg")?;
192182
///
193-
/// let response = client
194-
/// .media()
195-
/// .upload(&mime::IMAGE_JPEG, image, Some("my-cat.jpg".to_string()), None)
196-
/// .await?;
183+
/// let send_media_request = SendMediaUploadRequest::new(client.clone(), image)
184+
/// .with_content_type(mime::IMAGE_JPEG.essence_str().to_string())
185+
/// .with_filename(Some("my-cat.jpg"));
186+
///
187+
/// let response = client.media().upload(send_media_request).await?;
197188
///
198189
/// println!("Cat URI: {}", response.content_uri);
199190
/// # anyhow::Ok(()) };
200191
/// ```
201-
pub fn upload(
192+
pub async fn upload(
202193
&self,
203-
content_type: &Mime,
204-
data: Vec<u8>,
205-
filename: Option<String>,
206-
request_config: Option<RequestConfig>,
207-
) -> SendMediaUploadRequest {
208-
let request_config = request_config.unwrap_or_else(|| {
209-
self.client.request_config().timeout(Self::reasonable_upload_timeout(&data))
210-
});
211-
212-
let request = assign!(media::create_content::v3::Request::new(data), {
213-
filename,
214-
content_type: Some(content_type.essence_str().to_owned()),
194+
send_media_upload_request: SendMediaUploadRequest,
195+
) -> Result<media::create_content::v3::Response, Error> {
196+
let request_config = send_media_upload_request.request_config.unwrap_or_else(|| {
197+
self.client
198+
.request_config()
199+
.timeout(Self::reasonable_upload_timeout(send_media_upload_request.data()))
215200
});
216-
217-
let request = self.client.send(request).with_request_config(request_config);
218-
SendMediaUploadRequest::new(request)
201+
send_media_upload_request.with_request_config(Some(request_config)).await
219202
}
220203

221204
/// Returns a reasonable upload timeout for an upload, based on the size of
@@ -732,11 +715,12 @@ impl Media {
732715
let upload_thumbnail =
733716
self.upload_thumbnail(thumbnail, filename.clone(), send_progress.clone());
734717

735-
let upload_attachment = async move {
736-
self.upload(content_type, data, filename, None)
737-
.with_send_progress_observable(send_progress)
738-
.await
739-
};
718+
let send_media_request = SendMediaUploadRequest::new(self.client.clone(), data)
719+
.with_content_type(content_type.essence_str().to_owned())
720+
.with_filename(filename)
721+
.with_send_progress_observable(send_progress);
722+
723+
let upload_attachment = async move { self.upload(send_media_request).await };
740724

741725
let (thumbnail, response) = try_join(upload_thumbnail, upload_attachment).await?;
742726

@@ -758,10 +742,13 @@ impl Media {
758742
let (data, content_type, thumbnail_info) = thumbnail.into_parts();
759743

760744
let filename = filename.map(|name| format!("thumbnail-{name}"));
761-
let response = self
762-
.upload(&content_type, data, filename, None)
763-
.with_send_progress_observable(send_progress)
764-
.await?;
745+
746+
let send_media_request = SendMediaUploadRequest::new(self.client.clone(), data)
747+
.with_content_type(content_type.essence_str().to_owned())
748+
.with_filename(filename)
749+
.with_send_progress_observable(send_progress);
750+
751+
let response = self.upload(send_media_request).await?;
765752
let url = response.content_uri;
766753

767754
Ok(Some((MediaSource::Plain(url), thumbnail_info)))

crates/matrix-sdk/src/room/mod.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,8 @@ use crate::{
154154
},
155155
sync::RoomUpdate,
156156
utils::{IntoRawMessageLikeEventContent, IntoRawStateEventContent},
157-
BaseRoom, Client, Error, HttpResult, Result, RoomState, TransmissionProgress,
157+
BaseRoom, Client, Error, HttpResult, Result, RoomState, SendMediaUploadRequest,
158+
TransmissionProgress,
158159
};
159160
#[cfg(feature = "e2e-encryption")]
160161
use crate::{crypto::types::events::CryptoContextInfo, encryption::backups::BackupState};
@@ -2258,7 +2259,13 @@ impl Room {
22582259
let (media_source, thumbnail) = self
22592260
.client
22602261
.media()
2261-
.upload_plain_media_and_thumbnail(content_type, data.clone(), thumbnail, send_progress)
2262+
.upload_plain_media_and_thumbnail(
2263+
content_type,
2264+
data.clone(),
2265+
Some(filename.clone()),
2266+
thumbnail,
2267+
send_progress,
2268+
)
22622269
.await?;
22632270

22642271
if store_in_cache {
@@ -2515,7 +2522,10 @@ impl Room {
25152522
) -> Result<send_state_event::v3::Response> {
25162523
self.ensure_room_joined()?;
25172524

2518-
let upload_response = self.client.media().upload(mime, data, None, None).await?;
2525+
let send_media_request = SendMediaUploadRequest::new(self.client.clone(), data)
2526+
.with_content_type(mime.essence_str().to_owned());
2527+
2528+
let upload_response = self.client.media().upload(send_media_request).await?;
25192529
let mut info = info.unwrap_or_default();
25202530
info.blurhash = upload_response.blurhash;
25212531
info.mimetype = Some(mime.to_string());

0 commit comments

Comments
 (0)