Skip to content

Commit 10668f2

Browse files
authored
feat(send_queue): Implement sending of MSC4274 galleries (#4977)
This was broken out of #4838 and is a step towards implementing [MSC4274](matrix-org/matrix-spec-proposals#4274). * The entry point for sending galleries via the send queue is a new method [`RoomSendQueue::send_gallery`](https://github.com/matrix-org/matrix-rust-sdk/pull/4977/files#diff-8752e86459c22cff470d7ca617240dcbdf222c3c6c98be2af2e43ddec071154cR362) which is a generalization of `RoomSendQueue::send_attachment`. * `send_gallery` takes as input parameters a [`GalleryConfig`](https://github.com/matrix-org/matrix-rust-sdk/pull/4977/files#diff-d38d74cec6159cf769c32bed496199146175f05428fc23ab13bc2c629900da3eR283) (containing info about the gallery itself, such as its caption) and a vector of [`GalleryItemInfo`](https://github.com/matrix-org/matrix-rust-sdk/pull/4977/files#diff-d38d74cec6159cf769c32bed496199146175f05428fc23ab13bc2c629900da3eR355)s (containing info about each image / file / etc. in the gallery). * `send_gallery` creates the gallery event content via [`Room:make_message_event`](https://github.com/matrix-org/matrix-rust-sdk/pull/4977/files#diff-474f20e47fdcf60feac3f839a81f82fbb2c1fd0bb406388b0262adb60216ce3bR2281) which was renamed from `make_attachment_event` to reflect the fact that it creates general `msgtype` events now. * `send_gallery` maps the passed item infos into [`GalleryItemQueueInfo`](https://github.com/matrix-org/matrix-rust-sdk/pull/4977/files#diff-d569f54c7901b5cd660aae77045c80dabbd3c251f0fa5891530469f35941327aR2008)s. This additional struct allows grouping all the metadata for a single gallery item together. * Finally `send_gallery` invokes [`QueueStorage::push_gallery`](https://github.com/matrix-org/matrix-rust-sdk/pull/4977/files#diff-d569f54c7901b5cd660aae77045c80dabbd3c251f0fa5891530469f35941327aR1294) which is a generalization of `QueueStorage::push_media`. * `send_gallery` pushes upload requests for the media and thumbnails to the queue in a "daisy chain" manner. The first thumbnail (or media if no thumbnail exists) is pushed as a `QueuedRequestKind::MediaUpload`. The remaining thumbnails and media are pushed as `DependentQueuedRequestKind::UploadFileOrThumbnail`s while chaining each request to the previous one. * Finally a [`DependentQueuedRequestKind::FinishGallery`](https://github.com/matrix-org/matrix-rust-sdk/pull/4977/files#diff-42a1a7ebe2446d916c9e013293bafc5eb16fd89bfe89248e3877ef031cc83ef2R265) is pushed to finalize the gallery upload (analogous to the existing `FinishUpload` for single media uploads). * The `FinishGallery` request is handled in [`QueueStorage::handle_dependent_finish_gallery_upload`](https://github.com/matrix-org/matrix-rust-sdk/pull/4977/files#diff-8752e86459c22cff470d7ca617240dcbdf222c3c6c98be2af2e43ddec071154cR628) which was modeled after `handle_dependent_finish_upload`. * To be able to map the temporary event source for each gallery item to the final `AccumulatedSentMediaInfo`, a hash map is used. * Using the hash map, the gallery event is then updated to use the actual media sources via [`update_gallery_event_after_upload`](https://github.com/matrix-org/matrix-rust-sdk/pull/4977/files#diff-8752e86459c22cff470d7ca617240dcbdf222c3c6c98be2af2e43ddec071154cR104) and a new method [`Room::make_gallery_item_type`](https://github.com/matrix-org/matrix-rust-sdk/pull/4977/files#diff-474f20e47fdcf60feac3f839a81f82fbb2c1fd0bb406388b0262adb60216ce3bR2303) * An [integration test](https://github.com/matrix-org/matrix-rust-sdk/pull/4977/files#diff-21532ad5467a69489d7913b8da7afd4d618b7e357ce94f769e6e60e395b58055R2048) has been added to demonstrate the functionality. This is relatively large, unfortunately, but including everything needed to actually send the event made it possible to also add a test for it. It would be nice if the amount of new code could be reduced but I'm struggling a bit to find ways to integrate galleries with the existing media uploads further. - [x] Public API changes documented in changelogs (optional) --------- Signed-off-by: Johannes Marbach <[email protected]>
1 parent 2537f0a commit 10668f2

File tree

10 files changed

+1465
-267
lines changed

10 files changed

+1465
-267
lines changed

crates/matrix-sdk-base/src/store/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ mod send_queue;
7272
#[cfg(any(test, feature = "testing"))]
7373
pub use self::integration_tests::StateStoreIntegrationTests;
7474
#[cfg(feature = "unstable-msc4274")]
75-
pub use self::send_queue::AccumulatedSentMediaInfo;
75+
pub use self::send_queue::{AccumulatedSentMediaInfo, FinishGalleryItemInfo};
7676
pub use self::{
7777
memory_store::MemoryStore,
7878
send_queue::{

crates/matrix-sdk-base/src/store/send_queue.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,19 @@ pub enum DependentQueuedRequestKind {
258258
/// Information about the thumbnail, if present.
259259
thumbnail_info: Option<FinishUploadThumbnailInfo>,
260260
},
261+
262+
/// Finish a gallery upload by updating references to the media cache and
263+
/// sending the final gallery event with the remote MXC URIs.
264+
#[cfg(feature = "unstable-msc4274")]
265+
FinishGallery {
266+
/// Local echo for the event (containing the local MXC URIs).
267+
///
268+
/// `Box` the local echo so that it reduces the size of the whole enum.
269+
local_echo: Box<RoomMessageEventContent>,
270+
271+
/// Metadata about the gallery items.
272+
item_infos: Vec<FinishGalleryItemInfo>,
273+
},
261274
}
262275

263276
/// If parent_is_thumbnail_upload is missing, we assume the request is for a
@@ -285,6 +298,18 @@ pub struct FinishUploadThumbnailInfo {
285298
pub height: Option<UInt>,
286299
}
287300

301+
/// Detailed record about a file and thumbnail. When finishing a gallery
302+
/// upload, one [`FinishGalleryItemInfo`] will be used for each media in the
303+
/// gallery.
304+
#[cfg(feature = "unstable-msc4274")]
305+
#[derive(Clone, Debug, Serialize, Deserialize)]
306+
pub struct FinishGalleryItemInfo {
307+
/// Transaction id for the file upload.
308+
pub file_upload: OwnedTransactionId,
309+
/// Information about the thumbnail, if present.
310+
pub thumbnail_info: Option<FinishUploadThumbnailInfo>,
311+
}
312+
288313
/// A transaction id identifying a [`DependentQueuedRequest`] rather than its
289314
/// parent [`QueuedRequest`].
290315
///
@@ -369,6 +394,13 @@ pub struct AccumulatedSentMediaInfo {
369394
pub thumbnail: Option<MediaSource>,
370395
}
371396

397+
#[cfg(feature = "unstable-msc4274")]
398+
impl From<AccumulatedSentMediaInfo> for SentMediaInfo {
399+
fn from(value: AccumulatedSentMediaInfo) -> Self {
400+
Self { file: value.file, thumbnail: value.thumbnail, accumulated: vec![] }
401+
}
402+
}
403+
372404
/// A unique key (identifier) indicating that a transaction has been
373405
/// successfully sent to the server.
374406
///
@@ -442,6 +474,11 @@ impl DependentQueuedRequest {
442474
// This one graduates into a new media event.
443475
true
444476
}
477+
#[cfg(feature = "unstable-msc4274")]
478+
DependentQueuedRequestKind::FinishGallery { .. } => {
479+
// This one graduates into a new gallery event.
480+
true
481+
}
445482
}
446483
}
447484
}

crates/matrix-sdk/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ All notable changes to this project will be documented in this file.
3434
flag of the room if an unthreaded read receipt is sent.
3535
([#5055](https://github.com/matrix-org/matrix-rust-sdk/pull/5055))
3636
- `Client::is_user_ignored(&UserId)` can be used to check if a user is currently ignored. ([#5081](https://github.com/matrix-org/matrix-rust-sdk/pull/5081))
37+
- `RoomSendQueue::send_gallery` has been added to allow sending MSC4274-style media galleries
38+
via the send queue under the `unstable-msc4274` feature.
39+
([#4977](https://github.com/matrix-org/matrix-rust-sdk/pull/4977))
3740

3841
### Bug fixes
3942

crates/matrix-sdk/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ docsrs = ["e2e-encryption", "sqlite", "indexeddb", "sso-login", "qrcode"]
6161
experimental-share-history-on-invite = []
6262

6363
# Add support for inline media galleries via msgtypes
64-
unstable-msc4274 = ["matrix-sdk-base/unstable-msc4274"]
64+
unstable-msc4274 = ["ruma/unstable-msc4274", "matrix-sdk-base/unstable-msc4274"]
6565

6666
[dependencies]
6767
anyhow = { workspace = true, optional = true }

crates/matrix-sdk/src/attachment.rs

Lines changed: 123 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use ruma::{
2525
},
2626
Mentions,
2727
},
28-
OwnedTransactionId, TransactionId, UInt,
28+
OwnedTransactionId, UInt,
2929
};
3030

3131
use crate::room::reply::Reply;
@@ -219,8 +219,8 @@ impl AttachmentConfig {
219219
/// in its unsigned field as `transaction_id`. If not given, one is
220220
/// created for the message.
221221
#[must_use]
222-
pub fn txn_id(mut self, txn_id: &TransactionId) -> Self {
223-
self.txn_id = Some(txn_id.to_owned());
222+
pub fn txn_id(mut self, txn_id: OwnedTransactionId) -> Self {
223+
self.txn_id = Some(txn_id);
224224
self
225225
}
226226

@@ -236,21 +236,21 @@ impl AttachmentConfig {
236236
self
237237
}
238238

239-
/// Set the optional caption
239+
/// Set the optional caption.
240240
///
241241
/// # Arguments
242242
///
243-
/// * `caption` - The optional caption
243+
/// * `caption` - The optional caption.
244244
pub fn caption(mut self, caption: Option<String>) -> Self {
245245
self.caption = caption;
246246
self
247247
}
248248

249-
/// Set the optional formatted caption
249+
/// Set the optional formatted caption.
250250
///
251251
/// # Arguments
252252
///
253-
/// * `formatted_caption` - The optional formatted caption
253+
/// * `formatted_caption` - The optional formatted caption.
254254
pub fn formatted_caption(mut self, formatted_caption: Option<FormattedBody>) -> Self {
255255
self.formatted_caption = formatted_caption;
256256
self
@@ -260,7 +260,7 @@ impl AttachmentConfig {
260260
///
261261
/// # Arguments
262262
///
263-
/// * `mentions` - The mentions of the message
263+
/// * `mentions` - The mentions of the message.
264264
pub fn mentions(mut self, mentions: Option<Mentions>) -> Self {
265265
self.mentions = mentions;
266266
self
@@ -270,9 +270,123 @@ impl AttachmentConfig {
270270
///
271271
/// # Arguments
272272
///
273-
/// * `reply` - The reply information of the message
273+
/// * `reply` - The reply information of the message.
274274
pub fn reply(mut self, reply: Option<Reply>) -> Self {
275275
self.reply = reply;
276276
self
277277
}
278278
}
279+
280+
/// Configuration for sending a gallery.
281+
#[cfg(feature = "unstable-msc4274")]
282+
#[derive(Debug, Default)]
283+
pub struct GalleryConfig {
284+
pub(crate) txn_id: Option<OwnedTransactionId>,
285+
pub(crate) items: Vec<GalleryItemInfo>,
286+
pub(crate) caption: Option<String>,
287+
pub(crate) formatted_caption: Option<FormattedBody>,
288+
pub(crate) mentions: Option<Mentions>,
289+
pub(crate) reply: Option<Reply>,
290+
}
291+
292+
#[cfg(feature = "unstable-msc4274")]
293+
impl GalleryConfig {
294+
/// Create a new empty `GalleryConfig`.
295+
pub fn new() -> Self {
296+
Self::default()
297+
}
298+
299+
/// Set the transaction ID to send.
300+
///
301+
/// # Arguments
302+
///
303+
/// * `txn_id` - A unique ID that can be attached to a `MessageEvent` held
304+
/// in its unsigned field as `transaction_id`. If not given, one is
305+
/// created for the message.
306+
#[must_use]
307+
pub fn txn_id(mut self, txn_id: OwnedTransactionId) -> Self {
308+
self.txn_id = Some(txn_id);
309+
self
310+
}
311+
312+
/// Adds a media item to the gallery.
313+
///
314+
/// # Arguments
315+
///
316+
/// * `item` - Information about the item to be added.
317+
#[must_use]
318+
pub fn add_item(mut self, item: GalleryItemInfo) -> Self {
319+
self.items.push(item);
320+
self
321+
}
322+
323+
/// Set the optional caption.
324+
///
325+
/// # Arguments
326+
///
327+
/// * `caption` - The optional caption.
328+
pub fn caption(mut self, caption: Option<String>) -> Self {
329+
self.caption = caption;
330+
self
331+
}
332+
333+
/// Set the optional formatted caption.
334+
///
335+
/// # Arguments
336+
///
337+
/// * `formatted_caption` - The optional formatted caption.
338+
pub fn formatted_caption(mut self, formatted_caption: Option<FormattedBody>) -> Self {
339+
self.formatted_caption = formatted_caption;
340+
self
341+
}
342+
343+
/// Set the mentions of the message.
344+
///
345+
/// # Arguments
346+
///
347+
/// * `mentions` - The mentions of the message.
348+
pub fn mentions(mut self, mentions: Option<Mentions>) -> Self {
349+
self.mentions = mentions;
350+
self
351+
}
352+
353+
/// Set the reply information of the message.
354+
///
355+
/// # Arguments
356+
///
357+
/// * `reply` - The reply information of the message.
358+
pub fn reply(mut self, reply: Option<Reply>) -> Self {
359+
self.reply = reply;
360+
self
361+
}
362+
363+
/// Returns the number of media items in the gallery.
364+
pub fn len(&self) -> usize {
365+
self.items.len()
366+
}
367+
368+
/// Checks whether the gallery contains any media items or not.
369+
pub fn is_empty(&self) -> bool {
370+
self.items.is_empty()
371+
}
372+
}
373+
374+
#[cfg(feature = "unstable-msc4274")]
375+
#[derive(Debug)]
376+
/// Metadata for a gallery item
377+
pub struct GalleryItemInfo {
378+
/// The filename.
379+
pub filename: String,
380+
/// The mime type.
381+
pub content_type: mime::Mime,
382+
/// The binary data.
383+
pub data: Vec<u8>,
384+
/// The attachment info.
385+
pub attachment_info: AttachmentInfo,
386+
/// The caption.
387+
pub caption: Option<String>,
388+
/// The formatted caption.
389+
pub formatted_caption: Option<FormattedBody>,
390+
/// The thumbnail.
391+
pub thumbnail: Option<Thumbnail>,
392+
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,12 @@ pub(crate) fn update_media_caption(
269269
event.formatted = formatted_caption;
270270
true
271271
}
272+
#[cfg(feature = "unstable-msc4274")]
273+
MessageType::Gallery(event) => {
274+
event.body = caption.unwrap_or_default();
275+
event.formatted = formatted_caption;
276+
true
277+
}
272278
MessageType::Image(event) => {
273279
set_caption!(event, caption);
274280
event.formatted = formatted_caption;

0 commit comments

Comments
 (0)