mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 21:33:26 +00:00
Fix for overlapping timestamp when last block is not plainText (#5629)
* fix for the overlapping timestamp when the last block is not plain text * remove id and use offset as iterator in the foearch of the components
This commit is contained in:
@@ -37,8 +37,7 @@ extension AttributedString {
|
|||||||
.plainText
|
.plainText
|
||||||
}
|
}
|
||||||
|
|
||||||
components.append(AttributedStringBuilderComponent(id: String(attributedString.characters),
|
components.append(AttributedStringBuilderComponent(attributedString: attributedString,
|
||||||
attributedString: attributedString,
|
|
||||||
type: componentType))
|
type: componentType))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,14 +8,13 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct AttributedStringBuilderComponent: Hashable, Identifiable {
|
struct AttributedStringBuilderComponent: Hashable {
|
||||||
enum ComponentType {
|
enum ComponentType {
|
||||||
case plainText
|
case plainText
|
||||||
case blockquote
|
case blockquote
|
||||||
case codeBlock
|
case codeBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
let id: String
|
|
||||||
let attributedString: AttributedString
|
let attributedString: AttributedString
|
||||||
let type: ComponentType
|
let type: ComponentType
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -162,6 +162,30 @@ struct TimelineItemStyler_Previews: PreviewProvider, TestablePreview {
|
|||||||
sender: .test,
|
sender: .test,
|
||||||
content: .init(body: "😮"))
|
content: .init(body: "😮"))
|
||||||
|
|
||||||
|
static let endingWithBlockquote: TextRoomTimelineItem = {
|
||||||
|
let builder = AttributedStringBuilder(cacheKey: "preview", mentionBuilder: MentionBuilder())
|
||||||
|
let attributedString = builder.fromHTML("<p>Some text before</p><blockquote>A quoted line at the end</blockquote>")
|
||||||
|
return TextRoomTimelineItem(id: .randomEvent,
|
||||||
|
timestamp: .mock,
|
||||||
|
isOutgoing: true,
|
||||||
|
isEditable: false,
|
||||||
|
canBeRepliedTo: true,
|
||||||
|
sender: .test,
|
||||||
|
content: .init(body: "", formattedBody: attributedString))
|
||||||
|
}()
|
||||||
|
|
||||||
|
static let endingWithCodeblock: TextRoomTimelineItem = {
|
||||||
|
let builder = AttributedStringBuilder(cacheKey: "preview", mentionBuilder: MentionBuilder())
|
||||||
|
let attributedString = builder.fromHTML("<p>Some text before</p><pre><code>let x = 42</code></pre>")
|
||||||
|
return TextRoomTimelineItem(id: .randomEvent,
|
||||||
|
timestamp: .mock,
|
||||||
|
isOutgoing: true,
|
||||||
|
isEditable: false,
|
||||||
|
canBeRepliedTo: true,
|
||||||
|
sender: .test,
|
||||||
|
content: .init(body: "", formattedBody: attributedString))
|
||||||
|
}()
|
||||||
|
|
||||||
static var testView: some View {
|
static var testView: some View {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
TextRoomTimelineView(timelineItem: base)
|
TextRoomTimelineView(timelineItem: base)
|
||||||
@@ -170,6 +194,8 @@ struct TimelineItemStyler_Previews: PreviewProvider, TestablePreview {
|
|||||||
TextRoomTimelineView(timelineItem: sendingNonLast)
|
TextRoomTimelineView(timelineItem: sendingNonLast)
|
||||||
TextRoomTimelineView(timelineItem: sendingLast)
|
TextRoomTimelineView(timelineItem: sendingLast)
|
||||||
TextRoomTimelineView(timelineItem: failed)
|
TextRoomTimelineView(timelineItem: failed)
|
||||||
|
TextRoomTimelineView(timelineItem: endingWithBlockquote)
|
||||||
|
TextRoomTimelineView(timelineItem: endingWithCodeblock)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,11 +63,18 @@ struct FormattedBodyText: View {
|
|||||||
// component so that the bubble's natural size accommodates the overlaid
|
// component so that the bubble's natural size accommodates the overlaid
|
||||||
// timestamp, and TextKit decides whether to keep the timestamp on the last
|
// timestamp, and TextKit decides whether to keep the timestamp on the last
|
||||||
// line or push it to a new one.
|
// line or push it to a new one.
|
||||||
let components = attributedComponents
|
var components = attributedComponents
|
||||||
|
// When the body ends in a block component (blockquote/codeBlock) the overlaid
|
||||||
|
// timestamp would land on top of it, so append an empty trailing plain-text
|
||||||
|
// component that exists solely to reserve space for the timestamp underneath.
|
||||||
|
if trailingReservedSize != .zero, let last = components.last, last.type != .plainText {
|
||||||
|
components.append(AttributedStringBuilderComponent(attributedString: AttributedString(),
|
||||||
|
type: .plainText))
|
||||||
|
}
|
||||||
let lastPlainTextIndex = components.lastIndex { $0.type == .plainText }
|
let lastPlainTextIndex = components.lastIndex { $0.type == .plainText }
|
||||||
|
|
||||||
return TimelineBubbleLayout(spacing: 8) {
|
return TimelineBubbleLayout(spacing: 8) {
|
||||||
ForEach(Array(components.enumerated()), id: \.element.id) { index, component in
|
ForEach(Array(components.enumerated()), id: \.offset) { index, component in
|
||||||
switch component.type {
|
switch component.type {
|
||||||
case .blockquote:
|
case .blockquote:
|
||||||
BlockquoteView(attributedString: component.attributedString, mode: .rendering)
|
BlockquoteView(attributedString: component.attributedString, mode: .rendering)
|
||||||
@@ -88,10 +95,10 @@ struct FormattedBodyText: View {
|
|||||||
.timelineBubbleLayoutSize(.natural)
|
.timelineBubbleLayoutSize(.natural)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a second iteration through the components adding naturally sized versions of the
|
// 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.
|
// block quotes and code blocks which are used for layout calculations but won't be rendered.
|
||||||
ForEach(components) { component in
|
ForEach(Array(components.enumerated()), id: \.offset) { _, component in
|
||||||
switch component.type {
|
switch component.type {
|
||||||
case .blockquote:
|
case .blockquote:
|
||||||
BlockquoteView(attributedString: component.attributedString, mode: .layout)
|
BlockquoteView(attributedString: component.attributedString, mode: .layout)
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:67db610ecaf9e23c19dd893c6387e4b14fd71be69d31e921630aa65805fbd9c3
|
oid sha256:c01ec03d81cbf209252a5b6d9ee50d18211f1fd7735f049b5fdb4c9f8425122e
|
||||||
size 92903
|
size 117158
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:2d6d16dff4f49fa7fd5011e3381d109448d4d2db631488007b9d6aa53e7ce95a
|
oid sha256:3693e504ddecc2883790057b1f0c2ddd40170c5a2cd8ab522e27d361b8bf0a8e
|
||||||
size 98265
|
size 124281
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:6576a0cb6536ffeb069ae6a777eb5fbbfb922066c5c18078373d76b57d86cc55
|
oid sha256:ab7229d6a5f73bad5789ec03075170a25ae92ba020da68ca2b05014653c9732e
|
||||||
size 50453
|
size 71246
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:3a1b607e90d93f167665a093df07ae75cbcc7e907628856cef79679322bdbac5
|
oid sha256:a5d922f56785684a89937cc699c440349fd4ac6656052b67e000703ed36f24e9
|
||||||
size 54832
|
size 76911
|
||||||
|
|||||||
Reference in New Issue
Block a user