Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for rendering media captions in the timeline. #3429

Merged
merged 1 commit into from
Oct 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
Loading