mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 01:20:12 +00:00
Add support for rendering media captions in the timeline. (#3429)
This commit is contained in:
@@ -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)
|
||||
|
||||
+43
-9
@@ -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)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+35
-5
@@ -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)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+17
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f63e72b33f82f8b679dd8b1fc810078438914d70d395c23abd13571d01643597
|
||||
size 227546
|
||||
oid sha256:6cf2fd3de65756ae06e150b5353386e9b5bb554d18da2f77b0cf54246c3920eb
|
||||
size 285656
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f63e72b33f82f8b679dd8b1fc810078438914d70d395c23abd13571d01643597
|
||||
size 227546
|
||||
oid sha256:6cf2fd3de65756ae06e150b5353386e9b5bb554d18da2f77b0cf54246c3920eb
|
||||
size 285656
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:fb0bce1bf5571926c9ae760503e3aff11bfa962d646e257ab9481bc9496b95a6
|
||||
size 169079
|
||||
oid sha256:0b2d7c9d9bf570589eb48fbbb11bf11f2970229599a5be9b06d3f54e855e0d06
|
||||
size 224889
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:fb0bce1bf5571926c9ae760503e3aff11bfa962d646e257ab9481bc9496b95a6
|
||||
size 169079
|
||||
oid sha256:0b2d7c9d9bf570589eb48fbbb11bf11f2970229599a5be9b06d3f54e855e0d06
|
||||
size 224889
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:919c37cec762f3a5ead4e55e611e78bc3b6a52fdb6c2f2435d387511bdaf3bb3
|
||||
size 81177
|
||||
oid sha256:f0f9e472dd0a7675c25bc5856aca8f3e8ed94b157aa2dea3683e8a7cf17b588c
|
||||
size 94892
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:919c37cec762f3a5ead4e55e611e78bc3b6a52fdb6c2f2435d387511bdaf3bb3
|
||||
size 81177
|
||||
oid sha256:f0f9e472dd0a7675c25bc5856aca8f3e8ed94b157aa2dea3683e8a7cf17b588c
|
||||
size 94892
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:bbf8a9e38c8188356a7e9758b7afbcbc84c4b58b4ada32999e1591883c99704c
|
||||
size 40312
|
||||
oid sha256:8e4e25d87b4335854f8cd67072ef0e8e89a50a9a56dbc10b6cf01ee725731767
|
||||
size 52197
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:bbf8a9e38c8188356a7e9758b7afbcbc84c4b58b4ada32999e1591883c99704c
|
||||
size 40312
|
||||
oid sha256:8e4e25d87b4335854f8cd67072ef0e8e89a50a9a56dbc10b6cf01ee725731767
|
||||
size 52197
|
||||
|
||||
Reference in New Issue
Block a user