mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 06:55:59 +00:00
update all the timeline item views to use the new implemementation with nstextattachment
This commit is contained in:
+1
-1
@@ -17,7 +17,7 @@ struct AudioMediaEventsTimelineView: View {
|
||||
fileSize: timelineItem.content.fileSize,
|
||||
caption: timelineItem.content.caption,
|
||||
formattedCaption: timelineItem.content.formattedCaption,
|
||||
additionalWhitespaces: timelineItem.additionalWhitespaces(),
|
||||
trailingReservedSize: timelineItem.trailingReservedSize,
|
||||
isAudioFile: true)
|
||||
.accessibilityLabel(L10n.commonAudio)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ struct FileMediaEventsTimelineView: View {
|
||||
fileSize: timelineItem.content.fileSize,
|
||||
caption: timelineItem.content.caption,
|
||||
formattedCaption: timelineItem.content.formattedCaption,
|
||||
additionalWhitespaces: timelineItem.additionalWhitespaces())
|
||||
trailingReservedSize: timelineItem.trailingReservedSize)
|
||||
.accessibilityLabel(L10n.commonFile)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.bubbleBackground(isOutgoing: timelineItem.isOutgoing)
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ struct AudioRoomTimelineView: View {
|
||||
fileSize: timelineItem.content.fileSize,
|
||||
caption: timelineItem.content.caption,
|
||||
formattedCaption: timelineItem.content.formattedCaption,
|
||||
additionalWhitespaces: timelineItem.additionalWhitespaces(),
|
||||
trailingReservedSize: timelineItem.trailingReservedSize,
|
||||
shouldBoost: timelineItem.shouldBoost,
|
||||
isAudioFile: true) {
|
||||
context?.send(viewAction: .mediaTapped(itemID: timelineItem.id))
|
||||
|
||||
+4
-2
@@ -15,9 +15,11 @@ struct EmoteRoomTimelineView: View, TextBasedRoomTimelineViewProtocol {
|
||||
var body: some View {
|
||||
TimelineStyler(timelineItem: timelineItem) {
|
||||
if let attributedString = timelineItem.content.formattedBody {
|
||||
FormattedBodyText(attributedString: attributedString, additionalWhitespacesCount: timelineItem.additionalWhitespaces())
|
||||
FormattedBodyText(attributedString: attributedString,
|
||||
trailingReservedSize: timelineItem.trailingReservedSize)
|
||||
} else {
|
||||
FormattedBodyText(text: timelineItem.content.body, additionalWhitespacesCount: timelineItem.additionalWhitespaces())
|
||||
FormattedBodyText(text: timelineItem.content.body,
|
||||
trailingReservedSize: timelineItem.trailingReservedSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ struct FileRoomTimelineView: View {
|
||||
fileSize: timelineItem.content.fileSize,
|
||||
caption: timelineItem.content.caption,
|
||||
formattedCaption: timelineItem.content.formattedCaption,
|
||||
additionalWhitespaces: timelineItem.additionalWhitespaces(),
|
||||
trailingReservedSize: timelineItem.trailingReservedSize,
|
||||
shouldBoost: timelineItem.shouldBoost) {
|
||||
context?.send(viewAction: .mediaTapped(itemID: timelineItem.id))
|
||||
}
|
||||
@@ -35,7 +35,7 @@ struct MediaFileRoomTimelineContent: View {
|
||||
let fileSize: UInt?
|
||||
let caption: String?
|
||||
let formattedCaption: AttributedString?
|
||||
let additionalWhitespaces: Int
|
||||
var trailingReservedSize: CGSize = .zero
|
||||
var shouldBoost = false
|
||||
var isAudioFile = false
|
||||
|
||||
@@ -66,11 +66,11 @@ struct MediaFileRoomTimelineContent: View {
|
||||
|
||||
if let formattedCaption {
|
||||
FormattedBodyText(attributedString: formattedCaption,
|
||||
additionalWhitespacesCount: additionalWhitespaces,
|
||||
trailingReservedSize: trailingReservedSize,
|
||||
boostFontSize: shouldBoost)
|
||||
} else if let caption {
|
||||
FormattedBodyText(text: caption,
|
||||
additionalWhitespacesCount: additionalWhitespaces,
|
||||
trailingReservedSize: trailingReservedSize,
|
||||
boostFontSize: shouldBoost)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ struct FormattedBodyText: View {
|
||||
@Environment(\.layoutDirection) private var layoutDirection
|
||||
|
||||
private let attributedString: AttributedString
|
||||
private let additionalWhitespacesCount: Int
|
||||
private let trailingReservedSize: CGSize
|
||||
private let boostFontSize: Bool
|
||||
|
||||
private let defaultAttributesContainer: AttributeContainer = {
|
||||
@@ -22,14 +22,9 @@ struct FormattedBodyText: View {
|
||||
container.foregroundColor = UIColor.compound.textPrimary
|
||||
return container
|
||||
}()
|
||||
|
||||
|
||||
private var attributedComponents: [AttributedStringBuilderComponent] {
|
||||
var adjustedAttributedString = attributedString + AttributedString(additionalWhitespacesSuffix)
|
||||
|
||||
// If this is not a list, force the writing direction by adding the correct unicode character.
|
||||
if !String(attributedString.characters).starts(with: "\t") {
|
||||
adjustedAttributedString = AttributedString(layoutDirection.isolateLayoutUnicodeString) + adjustedAttributedString
|
||||
}
|
||||
var adjustedAttributedString = attributedString
|
||||
|
||||
// Required to allow the underlying TextView to use body font when no font is specifie in the AttributedString.
|
||||
adjustedAttributedString.mergeAttributes(defaultAttributesContainer, mergePolicy: .keepCurrent)
|
||||
@@ -44,24 +39,19 @@ struct FormattedBodyText: View {
|
||||
}
|
||||
|
||||
init(attributedString: AttributedString,
|
||||
additionalWhitespacesCount: Int = 0,
|
||||
trailingReservedSize: CGSize = .zero,
|
||||
boostFontSize: Bool = false) {
|
||||
self.attributedString = attributedString
|
||||
self.additionalWhitespacesCount = additionalWhitespacesCount
|
||||
self.trailingReservedSize = trailingReservedSize
|
||||
self.boostFontSize = boostFontSize
|
||||
}
|
||||
|
||||
init(text: String, additionalWhitespacesCount: Int = 0, boostFontSize: Bool = false) {
|
||||
init(text: String, trailingReservedSize: CGSize = .zero, boostFontSize: Bool = false) {
|
||||
self.init(attributedString: AttributedString(text),
|
||||
additionalWhitespacesCount: additionalWhitespacesCount,
|
||||
trailingReservedSize: trailingReservedSize,
|
||||
boostFontSize: boostFontSize)
|
||||
}
|
||||
|
||||
/// These is needed to create the slightly off inlined timestamp effect
|
||||
private var additionalWhitespacesSuffix: String {
|
||||
.generateBreakableWhitespaceEnd(whitespaceCount: additionalWhitespacesCount, layoutDirection: layoutDirection)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
layout
|
||||
.tint(.compound.textLinkExternal)
|
||||
@@ -71,36 +61,39 @@ struct FormattedBodyText: View {
|
||||
|
||||
/// The attributed components laid out for the bubbles timeline style.
|
||||
var layout: some View {
|
||||
TimelineBubbleLayout(spacing: 8) {
|
||||
ForEach(attributedComponents) { component in
|
||||
// Ignore if the string contains only the layout correction
|
||||
if String(component.attributedString.characters) == layoutDirection.isolateLayoutUnicodeString {
|
||||
EmptyView()
|
||||
} else {
|
||||
switch component.type {
|
||||
case .blockquote:
|
||||
BlockquoteView(attributedString: component.attributedString, mode: .rendering)
|
||||
.timelineBubbleLayoutSize(.bubbleWidth(mode: .rendering))
|
||||
case .codeBlock:
|
||||
CodeBlockView(attributedString: component.attributedString, mode: .rendering)
|
||||
.timelineBubbleLayoutSize(.bubbleWidth(mode: .rendering))
|
||||
.contextMenu {
|
||||
Button(L10n.actionCopy) {
|
||||
UIPasteboard.general.string = component.attributedString.string
|
||||
}
|
||||
// The trailing reserved area must only be applied to the last plain text
|
||||
// component so that the bubble's natural size accommodates the overlaid
|
||||
// timestamp, and TextKit decides whether to keep the timestamp on the last
|
||||
// line or push it to a new one.
|
||||
let components = attributedComponents
|
||||
let lastPlainTextIndex = components.lastIndex { $0.type == .plainText }
|
||||
|
||||
return TimelineBubbleLayout(spacing: 8) {
|
||||
ForEach(Array(components.enumerated()), id: \.element.id) { index, component in
|
||||
switch component.type {
|
||||
case .blockquote:
|
||||
BlockquoteView(attributedString: component.attributedString, mode: .rendering)
|
||||
.timelineBubbleLayoutSize(.bubbleWidth(mode: .rendering))
|
||||
case .codeBlock:
|
||||
CodeBlockView(attributedString: component.attributedString, mode: .rendering)
|
||||
.timelineBubbleLayoutSize(.bubbleWidth(mode: .rendering))
|
||||
.contextMenu {
|
||||
Button(L10n.actionCopy) {
|
||||
UIPasteboard.general.string = component.attributedString.string
|
||||
}
|
||||
case .plainText:
|
||||
MessageText(attributedString: component.attributedString)
|
||||
.padding(.horizontal, 4)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.timelineBubbleLayoutSize(.natural)
|
||||
}
|
||||
}
|
||||
case .plainText:
|
||||
MessageText(attributedString: component.attributedString,
|
||||
trailingReservedSize: index == lastPlainTextIndex ? trailingReservedSize : .zero)
|
||||
.padding(.horizontal, 4)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.timelineBubbleLayoutSize(.natural)
|
||||
}
|
||||
}
|
||||
|
||||
// Make a second iteration through the components adding naturally sized versions of the
|
||||
// block quotes and code blocks which are used for layout calculations but won't be rendered.
|
||||
ForEach(attributedComponents) { component in
|
||||
ForEach(components) { component in
|
||||
switch component.type {
|
||||
case .blockquote:
|
||||
BlockquoteView(attributedString: component.attributedString, mode: .layout)
|
||||
|
||||
+2
-2
@@ -32,11 +32,11 @@ struct ImageRoomTimelineView: View {
|
||||
|
||||
if let attributedCaption = timelineItem.content.formattedCaption {
|
||||
FormattedBodyText(attributedString: attributedCaption,
|
||||
additionalWhitespacesCount: timelineItem.additionalWhitespaces(),
|
||||
trailingReservedSize: timelineItem.trailingReservedSize,
|
||||
boostFontSize: timelineItem.shouldBoost)
|
||||
} else if let caption = timelineItem.content.caption {
|
||||
FormattedBodyText(text: caption,
|
||||
additionalWhitespacesCount: timelineItem.additionalWhitespaces(),
|
||||
trailingReservedSize: timelineItem.trailingReservedSize,
|
||||
boostFontSize: timelineItem.shouldBoost)
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -37,7 +37,8 @@ struct LocationRoomTimelineView: View {
|
||||
.aspectRatio(mapAspectRatio, contentMode: .fit)
|
||||
.clipped()
|
||||
} else {
|
||||
FormattedBodyText(text: timelineItem.body, additionalWhitespacesCount: timelineItem.additionalWhitespaces())
|
||||
FormattedBodyText(text: timelineItem.body,
|
||||
trailingReservedSize: timelineItem.trailingReservedSize)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -18,9 +18,11 @@ struct NoticeRoomTimelineView: View, TextBasedRoomTimelineViewProtocol {
|
||||
|
||||
Label {
|
||||
if let attributedString = timelineItem.content.formattedBody {
|
||||
FormattedBodyText(attributedString: attributedString, additionalWhitespacesCount: timelineItem.additionalWhitespaces())
|
||||
FormattedBodyText(attributedString: attributedString,
|
||||
trailingReservedSize: timelineItem.trailingReservedSize)
|
||||
} else {
|
||||
FormattedBodyText(text: timelineItem.content.body, additionalWhitespacesCount: timelineItem.additionalWhitespaces())
|
||||
FormattedBodyText(text: timelineItem.content.body,
|
||||
trailingReservedSize: timelineItem.trailingReservedSize)
|
||||
}
|
||||
} icon: {
|
||||
CompoundIcon(\.info, size: .small, relativeTo: .compound.bodyLG)
|
||||
|
||||
@@ -28,11 +28,11 @@ struct TextRoomTimelineView: View, TextBasedRoomTimelineViewProtocol {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
if let attributedString = timelineItem.content.formattedBody {
|
||||
FormattedBodyText(attributedString: attributedString,
|
||||
additionalWhitespacesCount: timelineItem.additionalWhitespaces(),
|
||||
trailingReservedSize: timelineItem.trailingReservedSize,
|
||||
boostFontSize: timelineItem.shouldBoost)
|
||||
} else {
|
||||
FormattedBodyText(text: timelineItem.body,
|
||||
additionalWhitespacesCount: timelineItem.additionalWhitespaces(),
|
||||
trailingReservedSize: timelineItem.trailingReservedSize,
|
||||
boostFontSize: timelineItem.shouldBoost)
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -33,11 +33,11 @@ struct VideoRoomTimelineView: View {
|
||||
|
||||
if let attributedCaption = timelineItem.content.formattedCaption {
|
||||
FormattedBodyText(attributedString: attributedCaption,
|
||||
additionalWhitespacesCount: timelineItem.additionalWhitespaces(),
|
||||
trailingReservedSize: timelineItem.trailingReservedSize,
|
||||
boostFontSize: timelineItem.shouldBoost)
|
||||
} else if let caption = timelineItem.content.caption {
|
||||
FormattedBodyText(text: caption,
|
||||
additionalWhitespacesCount: timelineItem.additionalWhitespaces(),
|
||||
trailingReservedSize: timelineItem.trailingReservedSize,
|
||||
boostFontSize: timelineItem.shouldBoost)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user