Render replies to gallery items (#5942)

This commit is contained in:
Mauro
2026-07-29 16:28:46 +02:00
committed by GitHub
parent 58fb7c4fcb
commit 704b3dbb29
5 changed files with 69 additions and 14 deletions
@@ -60,6 +60,8 @@ struct TimelineReplyView: View {
case .image(let content): return content.imageInfo.source
case .video(let content): return content.videoInfo.source
case .voice(let content): return content.source
// Only the first item is scanned as it's the only one the reply previews.
case .gallery(let content): return content.items.first?.mediaSource
default: return nil
}
}
@@ -75,6 +77,7 @@ struct TimelineReplyView: View {
case .file(let content): return content.thumbnailSource
case .image(let content): return content.thumbnailInfo?.source
case .video(let content): return content.thumbnailInfo?.source
case .gallery(let content): return content.items.first?.thumbnailSource
default: return nil
}
}
@@ -130,12 +133,7 @@ struct TimelineReplyView: View {
formattedBody: nil,
icon: .init(kind: .icon(\.locationPin)))
case .gallery(let content):
ReplyView(sender: sender,
plainBody: content.caption ?? content.body,
formattedBody: content.formattedCaption,
icon: content.items.first?.thumbnailSource.map { .init(kind: .mediaSource($0)) }
?? content.items.first?.mediaSource.map { .init(kind: .mediaSource($0)) }
?? .init(kind: .icon(\.attachment)))
GalleryReplyView(sender: sender, content: content)
}
case .poll(let question):
ReplyView(sender: sender,
@@ -159,6 +157,47 @@ struct TimelineReplyView: View {
}
}
/// A gallery previews its first attachment as though it had been sent on its own, counting its
/// media when they're all images/videos and its attachments otherwise.
private struct GalleryReplyView: View {
let sender: TimelineItemSender
let content: GalleryRoomTimelineItemContent
private var caption: String? {
content.caption?.isBlank == false ? content.caption : nil
}
private var isMediaGallery: Bool {
content.items.allSatisfy { $0.isImage || $0.isVideo }
}
private var placeholder: String {
if isMediaGallery {
L10n.commonGalleryReplyMediaItems(content.items.count)
} else {
L10n.commonGalleryReplyAttachments(content.items.count)
}
}
private var icon: ReplyView.Icon? {
guard let item = content.items.first else { return nil }
return switch item {
case .image(_, let itemContent): .init(kind: .mediaSource(itemContent.thumbnailInfo?.source ?? itemContent.imageInfo.source))
case .video(_, let itemContent): itemContent.thumbnailInfo.map { .init(kind: .mediaSource($0.source)) }
case .audio: .init(kind: .icon(\.audio))
case .file, .other: .init(kind: .icon(\.attachment))
}
}
var body: some View {
ReplyView(sender: sender,
plainBody: caption ?? placeholder,
formattedBody: caption == nil ? nil : content.formattedCaption,
icon: icon)
}
}
private struct LoadingReplyView: View {
var body: some View {
ReplyView(sender: .init(id: "@alice:matrix.org"), plainBody: "Hello world", formattedBody: nil)
@@ -402,6 +441,22 @@ struct TimelineReplyView_Previews: PreviewProvider, TestablePreview {
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Bob"),
eventID: "123",
eventContent: .message(.notice(.init(body: "", formattedBody: attributedStringWithEventOnRoomAliasMention))))),
TimelineReplyView(placement: .timeline,
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
eventID: "123",
eventContent: .message(.gallery(.init(body: "Gallery",
items: [.mockImage(index: 0),
.mockVideo(index: 1)]))))),
TimelineReplyView(placement: .timeline,
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
eventID: "123",
eventContent: .message(.gallery(.init(body: "Gallery",
caption: "A trip to remember 🌅",
items: [.mockImage(index: 0),
.mockVideo(index: 1),
.mockImage(index: 2)]))))),
TimelineReplyView(placement: .timeline,
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Bob"),
eventID: "123",
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:90589b29a858d016ef4e9b22951f58b7cc1e91b101d1190891e557fa8ceb9547
size 271039
oid sha256:48e2c170447a62a56f085126489c5ed819ee3377e2efc29502da4e7741bf3288
size 330696
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a119dbafa20c81937c68f725af6d4f0029647cd043264e6bf912cb0a10b0c0a7
size 280754
oid sha256:b30099d8d6e152af0c21f127bafe0f67ecb6552c983cafcaf3a942ad306b2ea5
size 345232
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0f4a10b76481e53d8f58810df4c2073f2ad71041712d10efbd2cff6cc67a548f
size 192308
oid sha256:4ee06e1dc82d0d4261ddce83611fa44275e87bee6117a5e4e1474e9165af5df3
size 242630
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fd00fd4e7354c7e840b67cce353ad4a157eea52060b80f7bac6e00aabf32936c
size 192329
oid sha256:bc701056fe46fe1c5f3c12e9698a2f95b79057d931b29060603b13bd2233df46
size 245641