Skip to content

Commit

Permalink
Add support for rendering media captions in the timeline.
Browse files Browse the repository at this point in the history
  • Loading branch information
pixlwave committed Oct 18, 2024
1 parent bd4ecdd commit 9e2586d
Show file tree
Hide file tree
Showing 13 changed files with 116 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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 ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Expand Down Expand Up @@ -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)))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}
}

Expand Down Expand Up @@ -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)))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 9e2586d

Please sign in to comment.