1#[cfg(feature = "unstable-msc4274")]
18use std::{collections::HashMap, iter::zip};
19
20use matrix_sdk_base::{
21 RoomState,
22 media::{
23 MediaFormat, MediaRequestParameters, MediaThumbnailSettings,
24 store::IgnoreMediaRetentionPolicy,
25 },
26 store::{
27 ChildTransactionId, DependentQueuedRequestKind, FinishUploadThumbnailInfo,
28 QueuedRequestKind, SentMediaInfo, SentRequestKey, SerializableEventContent,
29 },
30};
31#[cfg(feature = "unstable-msc4274")]
32use matrix_sdk_base::{
33 media::UniqueKey,
34 store::{AccumulatedSentMediaInfo, FinishGalleryItemInfo},
35};
36use mime::Mime;
37#[cfg(feature = "unstable-msc4274")]
38use ruma::events::room::message::{GalleryItemType, GalleryMessageEventContent};
39use ruma::{
40 MilliSecondsSinceUnixEpoch, OwnedTransactionId, TransactionId,
41 events::{
42 AnyMessageLikeEventContent, Mentions,
43 room::{
44 MediaSource, ThumbnailInfo,
45 message::{FormattedBody, MessageType, RoomMessageEventContent},
46 },
47 },
48};
49use tracing::{Span, debug, error, instrument, trace, warn};
50
51use super::{QueueStorage, QueueThumbnailInfo, RoomSendQueue, RoomSendQueueError};
52use crate::{
53 Client, Media, Room,
54 attachment::{AttachmentConfig, Thumbnail},
55 room::edit::update_media_caption,
56 send_queue::{
57 LocalEcho, LocalEchoContent, MediaHandles, RoomSendQueueStorageError, RoomSendQueueUpdate,
58 SendHandle,
59 },
60};
61#[cfg(feature = "unstable-msc4274")]
62use crate::{
63 attachment::{GalleryConfig, GalleryItemInfo},
64 send_queue::GalleryItemQueueInfo,
65};
66
67fn update_media_event_after_upload(echo: &mut RoomMessageEventContent, sent: SentMediaInfo) {
70 match &mut echo.msgtype {
73 MessageType::Audio(event) => {
74 event.source = sent.file;
75 }
76 MessageType::File(event) => {
77 event.source = sent.file;
78 if let Some(info) = event.info.as_mut() {
79 info.thumbnail_source = sent.thumbnail;
80 }
81 }
82 MessageType::Image(event) => {
83 event.source = sent.file;
84 if let Some(info) = event.info.as_mut() {
85 info.thumbnail_source = sent.thumbnail;
86 }
87 }
88 MessageType::Video(event) => {
89 event.source = sent.file;
90 if let Some(info) = event.info.as_mut() {
91 info.thumbnail_source = sent.thumbnail;
92 }
93 }
94
95 _ => {
96 error!("Invalid message type in database: {}", echo.msgtype());
100 debug_assert!(false, "invalid message type in database");
102 }
103 }
104}
105
106#[cfg(feature = "unstable-msc4274")]
109fn update_gallery_event_after_upload(
110 echo: &mut RoomMessageEventContent,
111 sent: HashMap<String, AccumulatedSentMediaInfo>,
112) {
113 let MessageType::Gallery(gallery) = &mut echo.msgtype else {
114 error!("Invalid gallery item types in database");
118 debug_assert!(false, "invalid item type in database {:?}", echo.msgtype());
120 return;
121 };
122
123 for itemtype in gallery.itemtypes.iter_mut() {
126 match itemtype {
127 GalleryItemType::Audio(event) => match sent.get(&event.source.unique_key()) {
128 Some(sent) => event.source = sent.file.clone(),
129 None => error!("key for item {:?} does not exist on gallery event", event.source),
130 },
131 GalleryItemType::File(event) => match sent.get(&event.source.unique_key()) {
132 Some(sent) => {
133 event.source = sent.file.clone();
134 if let Some(info) = event.info.as_mut() {
135 info.thumbnail_source = sent.thumbnail.clone();
136 }
137 }
138 None => error!("key for item {:?} does not exist on gallery event", event.source),
139 },
140 GalleryItemType::Image(event) => match sent.get(&event.source.unique_key()) {
141 Some(sent) => {
142 event.source = sent.file.clone();
143 if let Some(info) = event.info.as_mut() {
144 info.thumbnail_source = sent.thumbnail.clone();
145 }
146 }
147 None => error!("key for item {:?} does not exist on gallery event", event.source),
148 },
149 GalleryItemType::Video(event) => match sent.get(&event.source.unique_key()) {
150 Some(sent) => {
151 event.source = sent.file.clone();
152 if let Some(info) = event.info.as_mut() {
153 info.thumbnail_source = sent.thumbnail.clone();
154 }
155 }
156 None => error!("key for item {:?} does not exist on gallery event", event.source),
157 },
158
159 _ => {
160 error!("Invalid gallery item types in database");
164 debug_assert!(false, "invalid gallery item type in database {itemtype:?}");
166 }
167 }
168 }
169}
170
171#[derive(Default)]
172struct MediaCacheResult {
173 upload_thumbnail_txn: Option<OwnedTransactionId>,
174 event_thumbnail_info: Option<(MediaSource, Box<ThumbnailInfo>)>,
175 queue_thumbnail_info: Option<QueueThumbnailInfo>,
176}
177
178impl RoomSendQueue {
179 #[instrument(skip_all, fields(event_txn))]
199 pub async fn send_attachment(
200 &self,
201 filename: impl Into<String>,
202 content_type: Mime,
203 data: Vec<u8>,
204 mut config: AttachmentConfig,
205 ) -> Result<SendHandle, RoomSendQueueError> {
206 let Some(room) = self.inner.room.get() else {
207 return Err(RoomSendQueueError::RoomDisappeared);
208 };
209
210 if room.state() != RoomState::Joined {
211 return Err(RoomSendQueueError::RoomNotJoined);
212 }
213
214 let filename = filename.into();
215 let extra_content = config.extra_content.take();
216 let upload_file_txn = TransactionId::new();
217 let send_event_txn = config.txn_id.map_or_else(ChildTransactionId::new, Into::into);
218
219 Span::current().record("event_txn", tracing::field::display(&*send_event_txn));
220 debug!(filename, %content_type, %upload_file_txn, "sending an attachment");
221
222 let file_media_request = Media::make_local_file_media_request(&upload_file_txn);
223
224 let MediaCacheResult { upload_thumbnail_txn, event_thumbnail_info, queue_thumbnail_info } =
225 RoomSendQueue::cache_media(&room, data, config.thumbnail.take(), &file_media_request)
226 .await?;
227
228 let event_content = room
230 .make_media_event(
231 Room::make_attachment_type(
232 &content_type,
233 filename,
234 file_media_request.source.clone(),
235 config.caption,
236 config.info,
237 event_thumbnail_info,
238 ),
239 config.mentions,
240 config.reply,
241 )
242 .await
243 .map_err(|_| RoomSendQueueError::FailedToCreateAttachment)?;
244
245 let created_at = MilliSecondsSinceUnixEpoch::now();
246
247 self.inner
249 .queue
250 .push_media(
251 event_content.clone(),
252 content_type,
253 send_event_txn.clone().into(),
254 created_at,
255 upload_file_txn.clone(),
256 file_media_request,
257 queue_thumbnail_info,
258 extra_content.clone(),
259 )
260 .await?;
261
262 trace!("manager sends a media to the background task");
263
264 self.inner.notifier.notify_one();
265
266 let send_handle = SendHandle {
267 room: self.clone(),
268 transaction_id: send_event_txn.clone().into(),
269 media_handles: vec![MediaHandles { upload_thumbnail_txn, upload_file_txn }],
270 created_at,
271 };
272
273 self.send_update(RoomSendQueueUpdate::NewLocalEvent(LocalEcho {
274 transaction_id: send_event_txn.clone().into(),
275 content: LocalEchoContent::Event {
276 serialized_event: merge_extra_content(
277 SerializableEventContent::new(&event_content.into())
278 .map_err(RoomSendQueueStorageError::JsonSerialization)?,
279 extra_content,
280 )?,
281 send_handle: send_handle.clone(),
282 send_error: None,
283 },
284 }));
285
286 Ok(send_handle)
287 }
288
289 #[cfg(feature = "unstable-msc4274")]
309 #[instrument(skip_all, fields(event_txn))]
310 pub async fn send_gallery(
311 &self,
312 gallery: GalleryConfig,
313 ) -> Result<SendHandle, RoomSendQueueError> {
314 let Some(room) = self.inner.room.get() else {
315 return Err(RoomSendQueueError::RoomDisappeared);
316 };
317
318 if room.state() != RoomState::Joined {
319 return Err(RoomSendQueueError::RoomNotJoined);
320 }
321
322 if gallery.is_empty() {
323 return Err(RoomSendQueueError::EmptyGallery);
324 }
325
326 let send_event_txn =
327 gallery.txn_id.clone().map_or_else(ChildTransactionId::new, Into::into);
328
329 Span::current().record("event_txn", tracing::field::display(&*send_event_txn));
330
331 let mut item_types = Vec::with_capacity(gallery.len());
332 let mut item_queue_infos = Vec::with_capacity(gallery.len());
333 let mut media_handles = Vec::with_capacity(gallery.len());
334
335 for item_info in gallery.items {
336 let GalleryItemInfo { filename, content_type, data, .. } = item_info;
337
338 let upload_file_txn = TransactionId::new();
339
340 debug!(filename, %content_type, %upload_file_txn, "uploading a gallery attachment");
341
342 let file_media_request = Media::make_local_file_media_request(&upload_file_txn);
343
344 let MediaCacheResult {
345 upload_thumbnail_txn,
346 event_thumbnail_info,
347 queue_thumbnail_info,
348 } = RoomSendQueue::cache_media(&room, data, item_info.thumbnail, &file_media_request)
349 .await?;
350
351 item_types.push(Room::make_gallery_item_type(
352 &content_type,
353 filename,
354 file_media_request.source.clone(),
355 item_info.caption,
356 Some(item_info.attachment_info),
357 event_thumbnail_info,
358 ));
359
360 item_queue_infos.push(GalleryItemQueueInfo {
361 content_type,
362 upload_file_txn: upload_file_txn.clone(),
363 file_media_request,
364 thumbnail: queue_thumbnail_info,
365 });
366
367 media_handles.push(MediaHandles { upload_file_txn, upload_thumbnail_txn });
368 }
369
370 let (body, formatted) =
372 gallery.caption.map(|caption| (caption.body, caption.formatted)).unwrap_or_default();
373 let event_content = room
374 .make_media_event(
375 MessageType::Gallery(GalleryMessageEventContent::new(body, formatted, item_types)),
376 gallery.mentions,
377 gallery.reply,
378 )
379 .await
380 .map_err(|_| RoomSendQueueError::FailedToCreateGallery)?;
381
382 let created_at = MilliSecondsSinceUnixEpoch::now();
383
384 self.inner
386 .queue
387 .push_gallery(
388 event_content.clone(),
389 send_event_txn.clone().into(),
390 created_at,
391 item_queue_infos,
392 )
393 .await?;
394
395 trace!("manager sends a gallery to the background task");
396
397 self.inner.notifier.notify_one();
398
399 let send_handle = SendHandle {
400 room: self.clone(),
401 transaction_id: send_event_txn.clone().into(),
402 media_handles,
403 created_at,
404 };
405
406 self.send_update(RoomSendQueueUpdate::NewLocalEvent(LocalEcho {
407 transaction_id: send_event_txn.clone().into(),
408 content: LocalEchoContent::Event {
409 serialized_event: SerializableEventContent::new(&event_content.into())
410 .map_err(RoomSendQueueStorageError::JsonSerialization)?,
411 send_handle: send_handle.clone(),
412 send_error: None,
413 },
414 }));
415
416 Ok(send_handle)
417 }
418
419 async fn cache_media(
420 room: &Room,
421 data: Vec<u8>,
422 thumbnail: Option<Thumbnail>,
423 file_media_request: &MediaRequestParameters,
424 ) -> Result<MediaCacheResult, RoomSendQueueError> {
425 let client = room.client();
426 let media_store =
427 client.media_store().lock().await.map_err(RoomSendQueueStorageError::LockError)?;
428
429 media_store
431 .add_media_content(
432 file_media_request,
433 data,
434 IgnoreMediaRetentionPolicy::Yes,
436 )
437 .await
438 .map_err(RoomSendQueueStorageError::MediaStoreError)?;
439
440 if let Some(thumbnail) = thumbnail {
442 let txn = TransactionId::new();
443 trace!(upload_thumbnail_txn = %txn, "media has a thumbnail");
444
445 let (data, content_type, thumbnail_info) = thumbnail.into_parts();
448 let file_size = data.len();
449
450 let thumbnail_height = thumbnail_info.height;
451 let thumbnail_width = thumbnail_info.width;
452
453 let thumbnail_media_request = Media::make_local_file_media_request(&txn);
455 media_store
456 .add_media_content(
457 &thumbnail_media_request,
458 data,
459 IgnoreMediaRetentionPolicy::Yes,
461 )
462 .await
463 .map_err(RoomSendQueueStorageError::MediaStoreError)?;
464
465 Ok(MediaCacheResult {
466 upload_thumbnail_txn: Some(txn.clone()),
467 event_thumbnail_info: Some((
468 thumbnail_media_request.source.clone(),
469 thumbnail_info,
470 )),
471 queue_thumbnail_info: Some(QueueThumbnailInfo {
472 finish_upload_thumbnail_info: FinishUploadThumbnailInfo {
473 txn,
474 width: thumbnail_width,
475 height: thumbnail_height,
476 },
477 media_request_parameters: thumbnail_media_request,
478 content_type,
479 file_size,
480 }),
481 })
482 } else {
483 Ok(Default::default())
484 }
485 }
486}
487
488pub(super) fn merge_extra_content(
493 content: SerializableEventContent,
494 extra_content: Option<serde_json::Map<String, serde_json::Value>>,
495) -> Result<SerializableEventContent, RoomSendQueueStorageError> {
496 let Some(extra_content) = extra_content else {
497 return Ok(content);
498 };
499 if extra_content.is_empty() {
500 return Ok(content);
501 }
502
503 let (raw, event_type) = content.into_raw();
504 let mut object: serde_json::Map<String, serde_json::Value> =
505 raw.deserialize_as().map_err(RoomSendQueueStorageError::JsonSerialization)?;
506
507 for (key, value) in extra_content {
508 match object.entry(key) {
509 serde_json::map::Entry::Occupied(entry) => {
510 warn!(key = entry.key(), "extra content field shadowed by the event's own field");
511 }
512 serde_json::map::Entry::Vacant(entry) => {
513 entry.insert(value);
514 }
515 }
516 }
517
518 let raw = ruma::serde::Raw::from_json(
519 serde_json::value::to_raw_value(&object)
520 .map_err(RoomSendQueueStorageError::JsonSerialization)?,
521 );
522
523 Ok(SerializableEventContent::from_raw(raw, event_type))
524}
525
526impl QueueStorage {
527 #[allow(clippy::too_many_arguments)]
529 pub(super) async fn handle_dependent_finish_upload(
530 &self,
531 client: &Client,
532 event_txn: OwnedTransactionId,
533 parent_key: SentRequestKey,
534 mut local_echo: RoomMessageEventContent,
535 file_upload_txn: OwnedTransactionId,
536 thumbnail_info: Option<FinishUploadThumbnailInfo>,
537 extra_content: Option<serde_json::Map<String, serde_json::Value>>,
538 new_updates: &mut Vec<RoomSendQueueUpdate>,
539 ) -> Result<(), RoomSendQueueError> {
540 let sent_media = parent_key
542 .into_media()
543 .ok_or(RoomSendQueueError::StorageError(RoomSendQueueStorageError::InvalidParentKey))?;
544
545 update_media_cache_keys_after_upload(client, &file_upload_txn, thumbnail_info, &sent_media)
546 .await?;
547 update_media_event_after_upload(&mut local_echo, sent_media);
548
549 let new_content = merge_extra_content(
550 SerializableEventContent::new(&local_echo.into())
551 .map_err(RoomSendQueueStorageError::JsonSerialization)?,
552 extra_content,
553 )?;
554
555 new_updates.push(RoomSendQueueUpdate::ReplacedLocalEvent {
558 transaction_id: event_txn.clone(),
559 new_content: new_content.clone(),
560 });
561
562 trace!(%event_txn, "queueing media event after successfully uploading media(s)");
563
564 client
565 .state_store()
566 .save_send_queue_request(
567 &self.room_id,
568 event_txn,
569 MilliSecondsSinceUnixEpoch::now(),
570 new_content.into(),
571 Self::HIGH_PRIORITY,
572 )
573 .await
574 .map_err(RoomSendQueueStorageError::StateStoreError)?;
575
576 Ok(())
577 }
578
579 #[cfg(feature = "unstable-msc4274")]
582 #[allow(clippy::too_many_arguments)]
583 pub(super) async fn handle_dependent_finish_gallery_upload(
584 &self,
585 client: &Client,
586 event_txn: OwnedTransactionId,
587 parent_key: SentRequestKey,
588 mut local_echo: RoomMessageEventContent,
589 item_infos: Vec<FinishGalleryItemInfo>,
590 new_updates: &mut Vec<RoomSendQueueUpdate>,
591 ) -> Result<(), RoomSendQueueError> {
592 let sent_gallery = parent_key
594 .into_media()
595 .ok_or(RoomSendQueueError::StorageError(RoomSendQueueStorageError::InvalidParentKey))?;
596
597 let mut sent_media_vec = sent_gallery.accumulated;
598 sent_media_vec.push(AccumulatedSentMediaInfo {
599 file: sent_gallery.file,
600 thumbnail: sent_gallery.thumbnail,
601 });
602
603 let mut sent_infos = HashMap::new();
604
605 for (item_info, sent_media) in zip(item_infos, sent_media_vec) {
606 let FinishGalleryItemInfo { file_upload: file_upload_txn, thumbnail_info } = item_info;
607
608 let from_req = Media::make_local_file_media_request(&file_upload_txn);
611 sent_infos.insert(from_req.source.unique_key(), sent_media.clone());
612
613 update_media_cache_keys_after_upload(
614 client,
615 &file_upload_txn,
616 thumbnail_info,
617 &sent_media.into(),
618 )
619 .await?;
620 }
621
622 update_gallery_event_after_upload(&mut local_echo, sent_infos);
623
624 let new_content = SerializableEventContent::new(&local_echo.into())
625 .map_err(RoomSendQueueStorageError::JsonSerialization)?;
626
627 new_updates.push(RoomSendQueueUpdate::ReplacedLocalEvent {
630 transaction_id: event_txn.clone(),
631 new_content: new_content.clone(),
632 });
633
634 trace!(%event_txn, "queueing media event after successfully uploading media(s)");
635
636 client
637 .state_store()
638 .save_send_queue_request(
639 &self.room_id,
640 event_txn,
641 MilliSecondsSinceUnixEpoch::now(),
642 new_content.into(),
643 Self::HIGH_PRIORITY,
644 )
645 .await
646 .map_err(RoomSendQueueStorageError::StateStoreError)?;
647
648 Ok(())
649 }
650
651 #[allow(clippy::too_many_arguments)]
654 pub(super) async fn handle_dependent_file_or_thumbnail_upload(
655 &self,
656 client: &Client,
657 next_upload_txn: OwnedTransactionId,
658 parent_key: SentRequestKey,
659 content_type: String,
660 cache_key: MediaRequestParameters,
661 event_txn: OwnedTransactionId,
662 parent_is_thumbnail_upload: bool,
663 ) -> Result<(), RoomSendQueueError> {
664 let sent_media = parent_key
667 .into_media()
668 .ok_or(RoomSendQueueError::StorageError(RoomSendQueueStorageError::InvalidParentKey))?;
669
670 if parent_is_thumbnail_upload {
673 debug_assert!(sent_media.thumbnail.is_none());
674 if sent_media.thumbnail.is_some() {
675 warn!("unexpected thumbnail for a thumbnail!");
676 }
677 }
678
679 trace!(
680 related_to = %event_txn,
681 "done uploading file or thumbnail, now queuing the dependent file \
682 or thumbnail upload request",
683 );
684
685 #[cfg(feature = "unstable-msc4274")]
691 let accumulated = if parent_is_thumbnail_upload {
692 sent_media.accumulated
693 } else {
694 let mut accumulated = sent_media.accumulated;
695 accumulated.push(AccumulatedSentMediaInfo {
696 file: sent_media.file.clone(),
697 thumbnail: sent_media.thumbnail,
698 });
699 accumulated
700 };
701
702 let request = QueuedRequestKind::MediaUpload {
703 content_type,
704 cache_key,
705 thumbnail_source: parent_is_thumbnail_upload.then_some(sent_media.file),
708 related_to: event_txn,
709 #[cfg(feature = "unstable-msc4274")]
710 accumulated,
711 };
712
713 client
714 .state_store()
715 .save_send_queue_request(
716 &self.room_id,
717 next_upload_txn,
718 MilliSecondsSinceUnixEpoch::now(),
719 request,
720 Self::HIGH_PRIORITY,
721 )
722 .await
723 .map_err(RoomSendQueueStorageError::StateStoreError)?;
724
725 Ok(())
726 }
727
728 #[instrument(skip(self, handles))]
735 pub(super) async fn abort_upload(
736 &self,
737 event_txn: &TransactionId,
738 handles: &MediaHandles,
739 ) -> Result<bool, RoomSendQueueStorageError> {
740 let mut guard = self.store.lock().await;
741 let client = guard.client()?;
742
743 debug!("trying to abort an upload");
745
746 let store = client.state_store();
747
748 let upload_file_as_dependent = ChildTransactionId::from(handles.upload_file_txn.clone());
749 let event_as_dependent = ChildTransactionId::from(event_txn.to_owned());
750
751 let mut removed_dependent_upload = false;
752 let mut removed_dependent_event = false;
753
754 if let Some(thumbnail_txn) = &handles.upload_thumbnail_txn
755 && store.remove_send_queue_request(&self.room_id, thumbnail_txn).await?
756 {
757 trace!("could remove thumbnail request, removing 2 dependent requests now");
760
761 if let Some(info) = guard.being_sent.as_ref()
763 && info.transaction_id == *thumbnail_txn
764 {
765 let info = guard.being_sent.take().unwrap();
767 if info.cancel_upload() {
768 trace!("aborted ongoing thumbnail upload");
769 }
770 }
771
772 removed_dependent_upload = store
774 .remove_dependent_queued_request(&self.room_id, &upload_file_as_dependent)
775 .await?;
776
777 if !removed_dependent_upload {
778 warn!("unable to find the dependent file upload request");
779 }
780
781 removed_dependent_event =
782 store.remove_dependent_queued_request(&self.room_id, &event_as_dependent).await?;
783
784 if !removed_dependent_event {
785 warn!("unable to find the dependent media event upload request");
786 }
787 }
788
789 if !removed_dependent_upload {
796 if store.remove_send_queue_request(&self.room_id, &handles.upload_file_txn).await? {
797 trace!("could remove file upload request, removing 1 dependent request");
800
801 if let Some(info) = guard.being_sent.as_ref()
803 && info.transaction_id == handles.upload_file_txn
804 {
805 let info = guard.being_sent.take().unwrap();
807 if info.cancel_upload() {
808 trace!("aborted ongoing file upload");
809 }
810 }
811
812 if !store
814 .remove_dependent_queued_request(&self.room_id, &event_as_dependent)
815 .await?
816 {
817 warn!("unable to find the dependent media event upload request");
818 }
819 } else {
820 if !removed_dependent_event
825 && !store
826 .remove_dependent_queued_request(&self.room_id, &event_as_dependent)
827 .await?
828 {
829 debug!("uploads already happened => deferring to aborting an event sending");
832 return Ok(false);
833 }
834 }
835 }
836
837 {
840 let media_store = client.media_store().lock().await?;
841 media_store
842 .remove_media_content_for_uri(&Media::make_local_uri(&handles.upload_file_txn))
843 .await?;
844 if let Some(txn) = &handles.upload_thumbnail_txn {
845 media_store.remove_media_content_for_uri(&Media::make_local_uri(txn)).await?;
846 }
847 }
848
849 debug!("successfully aborted!");
850 Ok(true)
851 }
852
853 #[instrument(skip(self, caption, formatted_caption))]
854 pub(super) async fn edit_media_caption(
855 &self,
856 txn: &TransactionId,
857 caption: Option<String>,
858 formatted_caption: Option<FormattedBody>,
859 mentions: Option<Mentions>,
860 ) -> Result<Option<AnyMessageLikeEventContent>, RoomSendQueueStorageError> {
861 use RoomSendQueueStorageError::InvalidMediaCaptionEdit;
863
864 let guard = self.store.lock().await;
865 let client = guard.client()?;
866 let store = client.state_store();
867
868 {
876 let dependent_requests = store.load_dependent_queued_requests(&self.room_id).await?;
879
880 if let Some(found) =
881 dependent_requests.into_iter().find(|req| *req.own_transaction_id == *txn)
882 {
883 trace!("found the caption to edit in a dependent request");
884
885 let DependentQueuedRequestKind::FinishUpload {
886 mut local_echo,
887 file_upload,
888 thumbnail_info,
889 extra_content,
890 } = found.kind
891 else {
892 return Err(InvalidMediaCaptionEdit);
893 };
894
895 if !update_media_caption(&mut local_echo, caption, formatted_caption, mentions) {
896 return Err(InvalidMediaCaptionEdit);
897 }
898
899 let new_dependent_request = DependentQueuedRequestKind::FinishUpload {
900 local_echo: local_echo.clone(),
901 file_upload,
902 thumbnail_info,
903 extra_content,
904 };
905 store
906 .update_dependent_queued_request(
907 &self.room_id,
908 &found.own_transaction_id,
909 new_dependent_request,
910 )
911 .await?;
912
913 trace!("caption successfully updated");
914 return Ok(Some((*local_echo).into()));
915 }
916 }
917
918 let requests = store.load_send_queue_requests(&self.room_id).await?;
919 let Some(found) = requests.into_iter().find(|req| req.transaction_id == *txn) else {
920 return Ok(None);
922 };
923
924 trace!("found the caption to edit as a request");
925
926 let QueuedRequestKind::Event { content: serialized_content } = found.kind else {
927 return Err(InvalidMediaCaptionEdit);
928 };
929
930 let deserialized = serialized_content.deserialize()?;
931 let AnyMessageLikeEventContent::RoomMessage(mut content) = deserialized else {
932 return Err(InvalidMediaCaptionEdit);
933 };
934
935 if !update_media_caption(&mut content, caption, formatted_caption, mentions) {
936 return Err(InvalidMediaCaptionEdit);
937 }
938
939 let any_content: AnyMessageLikeEventContent = content.into();
940 let new_serialized = SerializableEventContent::new(&any_content.clone())?;
941
942 if let Some(being_sent) = guard.being_sent.as_ref()
944 && being_sent.transaction_id == *txn
945 {
946 store
948 .save_dependent_queued_request(
949 &self.room_id,
950 txn,
951 ChildTransactionId::new(),
952 MilliSecondsSinceUnixEpoch::now(),
953 DependentQueuedRequestKind::EditEvent { new_content: new_serialized },
954 )
955 .await?;
956
957 trace!("media event was being sent, pushed a dependent edit");
958 return Ok(Some(any_content));
959 }
960
961 store
963 .update_send_queue_request(
964 &self.room_id,
965 txn,
966 QueuedRequestKind::Event { content: new_serialized },
967 )
968 .await?;
969
970 trace!("media event was not being sent, updated local echo");
971 Ok(Some(any_content))
972 }
973}
974
975async fn update_media_cache_keys_after_upload(
978 client: &Client,
979 file_upload_txn: &OwnedTransactionId,
980 thumbnail_info: Option<FinishUploadThumbnailInfo>,
981 sent_media: &SentMediaInfo,
982) -> Result<(), RoomSendQueueError> {
983 let from_req = Media::make_local_file_media_request(file_upload_txn);
985
986 trace!(from = ?from_req.source, to = ?sent_media.file, "renaming media file key in cache store");
987 let media_store =
988 client.media_store().lock().await.map_err(RoomSendQueueStorageError::LockError)?;
989
990 media_store
992 .set_ignore_media_retention_policy(&from_req, IgnoreMediaRetentionPolicy::No)
993 .await
994 .map_err(RoomSendQueueStorageError::MediaStoreError)?;
995
996 media_store
997 .replace_media_key(
998 &from_req,
999 &MediaRequestParameters { source: sent_media.file.clone(), format: MediaFormat::File },
1000 )
1001 .await
1002 .map_err(RoomSendQueueStorageError::MediaStoreError)?;
1003
1004 if let Some((info, remote_thumbnail_source)) =
1006 thumbnail_info.as_ref().zip(sent_media.thumbnail.clone())
1007 {
1008 let from_request_params = Media::make_local_file_media_request(&info.txn);
1009
1010 if let Some((height, width)) = info.height.zip(info.width) {
1011 trace!(
1012 from = ?from_req.source,
1013 to = ?remote_thumbnail_source,
1014 height = u64::from(height),
1015 width = u64::from(width),
1016 "storing thumbnail as a thumbnail for the uploaded media, and a file in itself, in cache store"
1017 );
1018
1019 match media_store
1023 .get_media_content(&MediaRequestParameters {
1024 source: from_request_params.source.clone(),
1025 format: MediaFormat::File,
1026 })
1027 .await
1028 {
1029 Ok(Some(thumbnail_content)) => {
1030 media_store
1033 .add_media_content(
1034 &MediaRequestParameters {
1035 source: remote_thumbnail_source.clone(),
1036 format: MediaFormat::Thumbnail(MediaThumbnailSettings::new(
1037 width, height,
1038 )),
1039 },
1040 thumbnail_content,
1041 IgnoreMediaRetentionPolicy::No,
1042 )
1043 .await
1044 .map_err(RoomSendQueueStorageError::MediaStoreError)?;
1045 }
1046
1047 Ok(None) => {
1048 warn!(
1049 from = ?from_request_params.source,
1050 "unable to reload thumbnail content from media store: no content found",
1051 );
1052 }
1053
1054 Err(err) => {
1055 error!(
1058 from = ?from_request_params.source,
1059 "unable to reload thumbnail content from media store: {err}"
1060 );
1061 }
1062 }
1063 } else {
1064 trace!(from = ?from_req.source, to = ?remote_thumbnail_source, "only renaming thumbnail key to file in cache store");
1065 }
1066
1067 media_store
1069 .set_ignore_media_retention_policy(&from_request_params, IgnoreMediaRetentionPolicy::No)
1070 .await
1071 .map_err(RoomSendQueueStorageError::MediaStoreError)?;
1072
1073 media_store
1075 .replace_media_key(
1076 &from_request_params,
1077 &MediaRequestParameters {
1078 source: remote_thumbnail_source,
1079 format: MediaFormat::File,
1080 },
1081 )
1082 .await
1083 .map_err(RoomSendQueueStorageError::MediaStoreError)?;
1084 }
1085
1086 Ok(())
1087}