Support sending gallery messages behind a feature flag (#5909)

dds sending of multi-media "gallery" messages, gated behind the new galleryEnabled developer option (Developer Options → Room, off by default). Rendering of received galleries ships in a separate PR.

Wires sendGallery through TimelineProxy/TimelineController onto the SDK's Timeline.sendGallery.
MediaUploadPreviewScreen sends selected items as one gallery when the flag is on.
Co-authored with the original author of #5601.

NOTE: This PR allows to send gallery messages, but they won't be rendered without the gallery timeline item PR.
This commit is contained in:
Mauro
2026-07-21 17:25:30 +02:00
committed by GitHub
parent 711cb0a05c
commit b5b28b9986
14 changed files with 252 additions and 30 deletions
@@ -402,6 +402,14 @@ class TimelineController: TimelineControllerProtocol {
}
}
func sendGallery(itemInfos: [GalleryItemInfo],
caption: String?,
inReplyToEventID: String?) async -> Result<Void, TimelineControllerError> {
await activeTimeline.sendGallery(itemInfos: itemInfos,
caption: caption,
inReplyToEventID: inReplyToEventID).mapError(TimelineControllerError.timelineProxyError)
}
// MARK: - Polls
func createPoll(question: String, answers: [String], maxSelections: Int, pollKind: Poll.Kind) async -> Result<Void, TimelineControllerError> {
@@ -134,6 +134,10 @@ protocol TimelineControllerProtocol: Sendable {
waveform: [Float],
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineControllerError>
func sendGallery(itemInfos: [GalleryItemInfo],
caption: String?,
inReplyToEventID: String?) async -> Result<Void, TimelineControllerError>
// MARK: - Poll
func createPoll(question: String, answers: [String], maxSelections: Int, pollKind: Poll.Kind) async -> Result<Void, TimelineControllerError>
@@ -374,6 +374,27 @@ final class TimelineProxy: TimelineProxyProtocol {
return .success(())
}
func sendGallery(itemInfos: [GalleryItemInfo],
caption: String?,
inReplyToEventID: String?) async -> Result<Void, TimelineProxyError> {
MXLog.info("Sending gallery with \(itemInfos.count) items")
do {
let handle = try timeline.sendGallery(params: .init(caption: caption,
formattedCaption: nil, // Rust will build this from the caption's markdown.
mentions: nil,
inReplyTo: inReplyToEventID),
itemInfos: itemInfos)
try await handle.join()
MXLog.info("Finished sending gallery")
} catch {
MXLog.error("Failed sending gallery with error: \(error)")
return .failure(.sdkError(error))
}
return .success(())
}
func sendVoiceMessage(url: URL,
audioInfo: AudioInfo,
waveform: [Float],
@@ -110,6 +110,10 @@ protocol TimelineProxyProtocol: Sendable {
waveform: [Float],
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError>
func sendGallery(itemInfos: [GalleryItemInfo],
caption: String?,
inReplyToEventID: String?) async -> Result<Void, TimelineProxyError>
func sendReadReceipt(for eventID: String, type: ReceiptType) async -> Result<Void, TimelineProxyError>
func markAsRead(receiptType: ReceiptType) async -> Result<Void, TimelineProxyError>