Commit 0827ad2d24e9
tarball5 files changed+88-27
@@ -23,18 +23,20 @@ use matrix_sdk::{
2323
},2424
events::room::{2525
EncryptedFileHash, EncryptedFileHashAlgorithm, EncryptedFileInfo, MediaSource,26
- message::{AddMentions, ReplyWithinThread},26
+ message::{27
+ AddMentions, Relation, ReplyWithinThread, RoomMessageEventContentWithoutRelation,28
+ },2729
},2830
},2931
};3032
use matrix_sdk_crypto::{AttachmentDecryptor, MediaEncryptionInfo};31
-use ruma::MxcUri;33
+use ruma::{MxcUri, OwnedEventId};3234
use tokio::sync::Mutex;3335
3436
use super::{executor::EngineBoxFuture, url_preview::UrlPreviews};3537
use crate::{3638
AttachmentEncryption, AttachmentUpload, Error, ErrorKind, LinkPreview, Media, MediaFile,37
- MediaId, MediaKind, MediaLocation, MediaSize, TimelineId,39
+ MediaId, MediaKind, MediaLocation, MediaSize,3840
normalise::{MediaRegistration, MediaScope, MediaSourceInfo},3941
upload::prepare_upload,4042
};@@ -655,14 +657,35 @@ pub(crate) async fn queue_upload(
655657
upload: AttachmentUpload,656658
max_bytes: usize,657659
) -> Result<(), Error> {658
- let (upload, mime, mut config) = prepare_upload(upload, max_bytes)?;659
- if let TimelineId::Thread { root_event_id, .. } = &upload.timeline_id {660
- config.reply = Some(SdkReply {661
- event_id: root_event_id.clone(),660
+ let (mut upload, mime, mut config) = prepare_upload(upload, max_bytes)?;661
+ let thread_root = upload.timeline_id.thread_root().cloned();662
+ config.reply = match (upload.reply_to.take(), thread_root) {663
+ (Some(event_id), thread_root) => {664
+ let reply = || SdkReply {665
+ event_id: event_id.clone(),666
+ enforce_thread: if thread_root.is_some() {667
+ EnforceThread::Threaded(ReplyWithinThread::Yes)668
+ } else {669
+ EnforceThread::MaybeThreaded670
+ },671
+ add_mentions: if upload.notify_replied_to {672
+ AddMentions::Yes673
+ } else {674
+ AddMentions::No675
+ },676
+ };677
+ if let Some(thread_root) = &thread_root {678
+ check_reply_thread(&room, reply(), thread_root).await?;679
+ }680
+ Some(reply())681
+ }682
+ (None, Some(root_event_id)) => Some(SdkReply {683
+ event_id: root_event_id,662684
enforce_thread: EnforceThread::Threaded(ReplyWithinThread::No),663685
add_mentions: AddMentions::No,664
- });665
- }686
+ }),687
+ (None, None) => None,688
+ };666689
room.send_queue()667690
.send_attachment(upload.filename, mime, upload.bytes, config)668691
.await@@ -680,6 +703,31 @@ pub(crate) async fn queue_upload(
680703
})681704
}682705
706
+/// the send queue builds the relation itself, so a reply that would land in another thread has to be caught before it is queued707
+async fn check_reply_thread(708
+ room: &Room,709
+ reply: SdkReply,710
+ thread_root: &OwnedEventId,711
+) -> Result<(), Error> {712
+ let content = room713
+ .make_reply_event(RoomMessageEventContentWithoutRelation::text_plain(""), reply)714
+ .await715
+ .map_err(|source| {716
+ Error::with_source(ErrorKind::Timeline, "reply operation failed", source)717
+ })?;718
+ if matches!(719
+ content.relates_to.as_ref(),720
+ Some(Relation::Thread(thread)) if &thread.event_id == thread_root721
+ ) {722
+ Ok(())723
+ } else {724
+ Err(Error::new(725
+ ErrorKind::Invalid,726
+ "reply target does not belong to the selected thread",727
+ ))728
+ }729
+}730
+683731
/// least recently seen goes first. stale order slots are skipped by stamp rather than hunted down684732
struct MediaRegistry {685733
entries: HashMap<MediaId, (MediaRegistration, u64)>,@@ -7,11 +7,8 @@ use matrix_sdk::{
77
room::reply::{EnforceThread, Reply as SdkReply},88
ruma::{99
api::client::receipt::create_receipt::v3::ReceiptType,10
- events::{11
- Mentions,12
- room::message::{13
- AddMentions, Relation, ReplyWithinThread, RoomMessageEventContentWithoutRelation,14
- },10
+ events::room::message::{11
+ AddMentions, Relation, ReplyWithinThread, RoomMessageEventContentWithoutRelation,1512
},1613
},1714
};@@ -26,6 +23,7 @@ use crate::{
2623
engine::MediaService,2724
normalise::{MediaScope, normalise_timeline, normalise_timeline_diffs},2825
transport::{EventSender, Responder, SendEventError},26
+ upload::sdk_mentions,2927
};3028
3129
const PAGINATION_QUEUE: usize = 32;@@ -455,12 +453,7 @@ fn message_content(
455453
None if emote => RoomMessageEventContentWithoutRelation::emote_plain(content.body),456454
None => RoomMessageEventContentWithoutRelation::text_plain(content.body),457455
};458
- let content = content.add_mentions(Mentions::with_user_ids(mentions.user_ids));459
- if mentions.room {460
- content.add_mentions(Mentions::with_room_mention())461
- } else {462
- content463
- }456
+ content.add_mentions(sdk_mentions(mentions))464457
}465458
466459
pub(super) fn timeline_error(source: matrix_sdk_ui::timeline::Error) -> Error {@@ -334,7 +334,10 @@ pub struct AttachmentUpload {
334334
pub filename: String,335335
pub mimetype: String,336336
pub bytes: Vec<u8>,337
- pub caption: Option<String>,337
+ pub caption: Option<RichText>,338
+ pub mentions: MessageMentions,339
+ pub reply_to: Option<OwnedEventId>,340
+ pub notify_replied_to: bool,338341
pub details: Option<UploadDetails>,339342
pub thumbnail: Option<UploadThumbnail>,340343
}@@ -353,6 +356,9 @@ impl AttachmentUpload {
353356
mimetype: mimetype.into(),354357
bytes,355358
caption: None,359
+ mentions: MessageMentions::default(),360
+ reply_to: None,361
+ notify_replied_to: true,356362
details: None,357363
thumbnail: None,358364
}@@ -5,10 +5,23 @@ use matrix_sdk::{
55
AttachmentConfig, AttachmentInfo, BaseAudioInfo, BaseFileInfo, BaseImageInfo,66
BaseVideoInfo, Thumbnail,77
},8
- ruma::events::room::message::TextMessageEventContent,8
+ ruma::events::{Mentions, room::message::TextMessageEventContent},99
};1010
11
-use crate::{AttachmentUpload, Error, ErrorKind, UploadDetails};11
+use crate::{AttachmentUpload, Error, ErrorKind, MessageMentions, RichText, UploadDetails};12
+13
+pub(crate) fn sdk_mentions(mentions: MessageMentions) -> Mentions {14
+ let mut sdk = Mentions::with_user_ids(mentions.user_ids);15
+ sdk.room = mentions.room;16
+ sdk17
+}18
+19
+fn caption_content(caption: RichText) -> TextMessageEventContent {20
+ match caption.formatted_body {21
+ Some(formatted_body) => TextMessageEventContent::html(caption.body, formatted_body),22
+ None => TextMessageEventContent::plain(caption.body),23
+ }24
+}1225
1326
pub(crate) fn prepare_upload(1427
mut upload: AttachmentUpload,@@ -163,6 +176,7 @@ pub(crate) fn prepare_upload(
163176
let config = AttachmentConfig::new()164177
.info(info)165178
.thumbnail(thumbnail)166
- .caption(upload.caption.clone().map(TextMessageEventContent::plain));179
+ .caption(upload.caption.take().map(caption_content))180
+ .mentions(Some(sdk_mentions(std::mem::take(&mut upload.mentions))));167181
Ok((upload, mimetype, config))168182
}@@ -4,8 +4,8 @@ use std::{sync::Arc, time::Duration};
44
55
use macltok::{66
AttachmentUpload, ClientHandle, Engine, EngineConfig, EngineError, ErrorKind, Event,7
- EventStream, MediaFile, MediaId, MediaLocation, MediaSize, UploadDetails, UploadOutcome,8
- UploadThumbnail,7
+ EventStream, MediaFile, MediaId, MediaLocation, MediaSize, RichText, UploadDetails,8
+ UploadOutcome, UploadThumbnail,99
};1010
use matrix_sdk::{1111
media::{MediaFormat, MediaRequestParameters},@@ -397,7 +397,7 @@ async fn media_upload_is_queued_and_sent() {
397397
"image/jpeg",398398
b"photo data".to_vec(),399399
);400
- upload.caption = Some("holiday".to_owned());400
+ upload.caption = Some(RichText::plain("holiday"));401401
harness.handle.upload(upload).await.unwrap();402402
403403
tokio::time::timeout(Duration::from_secs(5), async {