Add support for sending media within threads.

This patch add the `threadRootEventID` as a parameter to media sending functions and forwards the timeline thread root between various application states to the place where its needed.
The 2 missing items are Locations and Polls which currently don't have any support on the Rust side for being sent within threads. That will come in a separate PR.
This commit is contained in:
Stefan Ceriu
2025-06-10 17:18:32 +03:00
committed by Stefan Ceriu
parent 6481093107
commit 558cdf5be3
18 changed files with 340 additions and 247 deletions
@@ -229,15 +229,22 @@ final class TimelineProxy: TimelineProxyProtocol {
func sendAudio(url: URL,
audioInfo: AudioInfo,
caption: String?,
threadRootEventID: String?,
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
MXLog.info("Sending audio")
let replyParameters: ReplyParameters? = if let threadRootEventID {
ReplyParameters(eventId: threadRootEventID, enforceThread: true, replyWithinThread: false)
} else {
nil
}
do {
let handle = try timeline.sendAudio(params: .init(source: .file(filename: url.path(percentEncoded: false)),
caption: caption,
formattedCaption: nil, // Rust will build this from the caption's markdown.
mentions: nil,
replyParams: nil,
replyParams: replyParameters,
useSendQueue: true),
audioInfo: audioInfo,
progressWatcher: nil)
@@ -257,15 +264,22 @@ final class TimelineProxy: TimelineProxyProtocol {
func sendFile(url: URL,
fileInfo: FileInfo,
caption: String?,
threadRootEventID: String?,
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
MXLog.info("Sending file")
let replyParameters: ReplyParameters? = if let threadRootEventID {
ReplyParameters(eventId: threadRootEventID, enforceThread: true, replyWithinThread: false)
} else {
nil
}
do {
let handle = try timeline.sendFile(params: .init(source: .file(filename: url.path(percentEncoded: false)),
caption: caption,
formattedCaption: nil, // Rust will build this from the caption's markdown.
mentions: nil,
replyParams: nil,
replyParams: replyParameters,
useSendQueue: true),
fileInfo: fileInfo,
progressWatcher: nil)
@@ -286,15 +300,22 @@ final class TimelineProxy: TimelineProxyProtocol {
thumbnailURL: URL,
imageInfo: ImageInfo,
caption: String?,
threadRootEventID: String?,
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
MXLog.info("Sending image")
let replyParameters: ReplyParameters? = if let threadRootEventID {
ReplyParameters(eventId: threadRootEventID, enforceThread: true, replyWithinThread: false)
} else {
nil
}
do {
let handle = try timeline.sendImage(params: .init(source: .file(filename: url.path(percentEncoded: false)),
caption: caption,
formattedCaption: nil, // Rust will build this from the caption's markdown.
mentions: nil,
replyParams: nil,
replyParams: replyParameters,
useSendQueue: true),
thumbnailPath: thumbnailURL.path(percentEncoded: false),
imageInfo: imageInfo,
@@ -316,7 +337,8 @@ final class TimelineProxy: TimelineProxyProtocol {
geoURI: GeoURI,
description: String?,
zoomLevel: UInt8?,
assetType: AssetType?) async -> Result<Void, TimelineProxyError> {
assetType: AssetType?,
threadRootEventID: String?) async -> Result<Void, TimelineProxyError> {
MXLog.info("Sending location")
await timeline.sendLocation(body: body,
@@ -334,15 +356,22 @@ final class TimelineProxy: TimelineProxyProtocol {
thumbnailURL: URL,
videoInfo: VideoInfo,
caption: String?,
threadRootEventID: String?,
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
MXLog.info("Sending video")
let replyParameters: ReplyParameters? = if let threadRootEventID {
ReplyParameters(eventId: threadRootEventID, enforceThread: true, replyWithinThread: false)
} else {
nil
}
do {
let handle = try timeline.sendVideo(params: .init(source: .file(filename: url.path(percentEncoded: false)),
caption: caption,
formattedCaption: nil,
mentions: nil,
replyParams: nil,
replyParams: replyParameters,
useSendQueue: true),
thumbnailPath: thumbnailURL.path(percentEncoded: false),
videoInfo: videoInfo,
@@ -363,15 +392,22 @@ final class TimelineProxy: TimelineProxyProtocol {
func sendVoiceMessage(url: URL,
audioInfo: AudioInfo,
waveform: [UInt16],
threadRootEventID: String?,
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
MXLog.info("Sending voice message")
let replyParameters: ReplyParameters? = if let threadRootEventID {
ReplyParameters(eventId: threadRootEventID, enforceThread: true, replyWithinThread: false)
} else {
nil
}
do {
let handle = try timeline.sendVoiceMessage(params: .init(source: .file(filename: url.path(percentEncoded: false)),
caption: nil,
formattedCaption: nil,
mentions: nil,
replyParams: nil,
replyParams: replyParameters,
useSendQueue: true),
audioInfo: audioInfo,
waveform: waveform,
@@ -481,7 +517,9 @@ final class TimelineProxy: TimelineProxyProtocol {
// MARK: - Polls
func createPoll(question: String, answers: [String], pollKind: Poll.Kind) async -> Result<Void, TimelineProxyError> {
func createPoll(question: String, answers: [String],
pollKind: Poll.Kind,
threadRootEventID: String? = nil) async -> Result<Void, TimelineProxyError> {
MXLog.info("Creating poll")
do {