mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-10 13:37:11 +00:00
Add support for rendering media captions in the timeline. (#3429)
This commit is contained in:
parent
201e3b0c9c
commit
a0c81cf393
@ -257,7 +257,7 @@ private extension EventBasedTimelineItemProtocol {
|
||||
switch self {
|
||||
case is ImageRoomTimelineItem, is VideoRoomTimelineItem:
|
||||
// In case a reply detail or a thread decorator is present we render the color and the padding
|
||||
return self.replyDetails != nil || self.isThreaded ? defaultColor : nil
|
||||
return self.replyDetails != nil || self.isThreaded || self.hasMediaCaption ? defaultColor : nil
|
||||
default:
|
||||
return defaultColor
|
||||
}
|
||||
@ -283,8 +283,7 @@ private extension EventBasedTimelineItemProtocol {
|
||||
// In case a reply detail or a thread decorator is present we render the color and the padding
|
||||
case is ImageRoomTimelineItem,
|
||||
is VideoRoomTimelineItem:
|
||||
return self.replyDetails != nil ||
|
||||
self.isThreaded ? defaultInsets : .zero
|
||||
return self.replyDetails != nil || self.isThreaded || self.hasMediaCaption ? defaultInsets : .zero
|
||||
case let locationTimelineItem as LocationRoomTimelineItem:
|
||||
return locationTimelineItem.content.geoURI == nil ||
|
||||
self.replyDetails != nil ||
|
||||
|
@ -152,9 +152,9 @@ private extension TimelineItemSendInfo {
|
||||
layoutType = switch timelineItem {
|
||||
case is TextBasedRoomTimelineItem:
|
||||
.overlay(capsuleStyle: false)
|
||||
case is ImageRoomTimelineItem,
|
||||
is VideoRoomTimelineItem,
|
||||
is StickerRoomTimelineItem:
|
||||
case let message as EventBasedMessageTimelineItemProtocol where message is ImageRoomTimelineItem || message is VideoRoomTimelineItem:
|
||||
.overlay(capsuleStyle: !message.hasMediaCaption)
|
||||
case is StickerRoomTimelineItem:
|
||||
.overlay(capsuleStyle: true)
|
||||
case let locationTimelineItem as LocationRoomTimelineItem:
|
||||
.overlay(capsuleStyle: locationTimelineItem.content.geoURI != nil)
|
||||
|
@ -12,18 +12,35 @@ struct ImageRoomTimelineView: View {
|
||||
@EnvironmentObject private var context: TimelineViewModel.Context
|
||||
let timelineItem: ImageRoomTimelineItem
|
||||
|
||||
var hasMediaCaption: Bool { timelineItem.content.caption != nil }
|
||||
|
||||
var body: some View {
|
||||
TimelineStyler(timelineItem: timelineItem) {
|
||||
LoadableImage(mediaSource: source,
|
||||
mediaType: .timelineItem,
|
||||
blurhash: timelineItem.content.blurhash,
|
||||
mediaProvider: context.mediaProvider) {
|
||||
placeholder
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
LoadableImage(mediaSource: source,
|
||||
mediaType: .timelineItem,
|
||||
blurhash: timelineItem.content.blurhash,
|
||||
mediaProvider: context.mediaProvider) {
|
||||
placeholder
|
||||
}
|
||||
.timelineMediaFrame(height: timelineItem.content.height,
|
||||
aspectRatio: timelineItem.content.aspectRatio)
|
||||
.accessibilityElement(children: .ignore)
|
||||
.accessibilityLabel(L10n.commonImage)
|
||||
// This clip shape is distinct from the one in the styler as that one
|
||||
// operates on the entire message so wouldn't round the bottom corners.
|
||||
.clipShape(RoundedRectangle(cornerRadius: hasMediaCaption ? 6 : 0))
|
||||
|
||||
if let attributedCaption = timelineItem.content.formattedCaption {
|
||||
FormattedBodyText(attributedString: attributedCaption,
|
||||
additionalWhitespacesCount: timelineItem.additionalWhitespaces(),
|
||||
boostEmojiSize: true)
|
||||
} else if let caption = timelineItem.content.caption {
|
||||
FormattedBodyText(text: caption,
|
||||
additionalWhitespacesCount: timelineItem.additionalWhitespaces(),
|
||||
boostEmojiSize: true)
|
||||
}
|
||||
}
|
||||
.timelineMediaFrame(height: timelineItem.content.height,
|
||||
aspectRatio: timelineItem.content.aspectRatio)
|
||||
.accessibilityElement(children: .ignore)
|
||||
.accessibilityLabel(L10n.commonImage)
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,6 +104,23 @@ struct ImageRoomTimelineView_Previews: PreviewProvider, TestablePreview {
|
||||
aspectRatio: 0.7,
|
||||
blurhash: "L%KUc%kqS$RP?Ks,WEf8OlrqaekW",
|
||||
contentType: .gif)))
|
||||
|
||||
ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: .randomEvent,
|
||||
timestamp: "Now",
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
canBeRepliedTo: true,
|
||||
isThreaded: false,
|
||||
sender: .init(id: "Bob"),
|
||||
content: .init(filename: "Blurhashed.jpg",
|
||||
caption: "This is a great image 😎",
|
||||
source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/png"),
|
||||
thumbnailSource: nil,
|
||||
width: 50,
|
||||
height: 50,
|
||||
aspectRatio: 1,
|
||||
blurhash: "L%KUc%kqS$RP?Ks,WEf8OlrqaekW",
|
||||
contentType: .gif)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,13 +12,30 @@ struct VideoRoomTimelineView: View {
|
||||
@EnvironmentObject private var context: TimelineViewModel.Context
|
||||
let timelineItem: VideoRoomTimelineItem
|
||||
|
||||
private var hasMediaCaption: Bool { timelineItem.content.caption != nil }
|
||||
|
||||
var body: some View {
|
||||
TimelineStyler(timelineItem: timelineItem) {
|
||||
thumbnail
|
||||
.timelineMediaFrame(height: timelineItem.content.height,
|
||||
aspectRatio: timelineItem.content.aspectRatio)
|
||||
.accessibilityElement(children: .ignore)
|
||||
.accessibilityLabel(L10n.commonVideo)
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
thumbnail
|
||||
.timelineMediaFrame(height: timelineItem.content.height,
|
||||
aspectRatio: timelineItem.content.aspectRatio)
|
||||
.accessibilityElement(children: .ignore)
|
||||
.accessibilityLabel(L10n.commonVideo)
|
||||
// This clip shape is distinct from the one in the styler as that one
|
||||
// operates on the entire message so wouldn't round the bottom corners.
|
||||
.clipShape(RoundedRectangle(cornerRadius: hasMediaCaption ? 6 : 0))
|
||||
|
||||
if let attributedCaption = timelineItem.content.formattedCaption {
|
||||
FormattedBodyText(attributedString: attributedCaption,
|
||||
additionalWhitespacesCount: timelineItem.additionalWhitespaces(),
|
||||
boostEmojiSize: true)
|
||||
} else if let caption = timelineItem.content.caption {
|
||||
FormattedBodyText(text: caption,
|
||||
additionalWhitespacesCount: timelineItem.additionalWhitespaces(),
|
||||
boostEmojiSize: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,6 +117,19 @@ struct VideoRoomTimelineView_Previews: PreviewProvider, TestablePreview {
|
||||
thumbnailSource: nil,
|
||||
aspectRatio: 0.7,
|
||||
blurhash: "L%KUc%kqS$RP?Ks,WEf8OlrqaekW")))
|
||||
|
||||
VideoRoomTimelineView(timelineItem: VideoRoomTimelineItem(id: .randomEvent,
|
||||
timestamp: "Now",
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
canBeRepliedTo: true,
|
||||
isThreaded: false,
|
||||
sender: .init(id: "Bob"),
|
||||
content: .init(filename: "video.mp4",
|
||||
caption: "This is a caption",
|
||||
duration: 21,
|
||||
source: nil,
|
||||
thumbnailSource: nil)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,3 +24,20 @@ protocol EventBasedMessageTimelineItemProtocol: EventBasedTimelineItemProtocol {
|
||||
var contentType: EventBasedMessageTimelineItemContentType { get }
|
||||
var isThreaded: Bool { get }
|
||||
}
|
||||
|
||||
extension EventBasedMessageTimelineItemProtocol {
|
||||
var hasMediaCaption: Bool {
|
||||
switch contentType {
|
||||
case .audio(let content):
|
||||
content.caption != nil
|
||||
case .file(let content):
|
||||
content.caption != nil
|
||||
case .image(let content):
|
||||
content.caption != nil
|
||||
case .video(let content):
|
||||
content.caption != nil
|
||||
case .emote, .notice, .text, .location, .voice:
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageRoomTimelineView-iPad-en-GB.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageRoomTimelineView-iPad-en-GB.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageRoomTimelineView-iPad-pseudo.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageRoomTimelineView-iPad-pseudo.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageRoomTimelineView-iPhone-16-en-GB.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageRoomTimelineView-iPhone-16-en-GB.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageRoomTimelineView-iPhone-16-pseudo.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageRoomTimelineView-iPhone-16-pseudo.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoRoomTimelineView-iPad-en-GB.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoRoomTimelineView-iPad-en-GB.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoRoomTimelineView-iPad-pseudo.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoRoomTimelineView-iPad-pseudo.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoRoomTimelineView-iPhone-16-en-GB.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoRoomTimelineView-iPhone-16-en-GB.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoRoomTimelineView-iPhone-16-pseudo.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoRoomTimelineView-iPhone-16-pseudo.1.png
(Stored with Git LFS)
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user