From a248bdbd95c2f2c2a310c2d5de7e1394c04fbb87 Mon Sep 17 00:00:00 2001 From: Mauro <34335419+Velin92@users.noreply.github.com> Date: Mon, 11 Dec 2023 16:43:31 +0100 Subject: [PATCH] Fix for line breaks (#2224) --- .../Sources/Other/Extensions/String.swift | 24 +++++++++++++++++++ .../HTMLParsing/AttributedStringBuilder.swift | 6 +++-- .../View/Timeline/FormattedBodyText.swift | 3 ++- UnitTests/Sources/StringTests.swift | 23 ++++++++++++++++++ .../PreviewTests/test_formattedBodyText.1.png | 4 ++-- 5 files changed, 55 insertions(+), 5 deletions(-) diff --git a/ElementX/Sources/Other/Extensions/String.swift b/ElementX/Sources/Other/Extensions/String.swift index d319ab666..10aa796d9 100644 --- a/ElementX/Sources/Other/Extensions/String.swift +++ b/ElementX/Sources/Other/Extensions/String.swift @@ -87,3 +87,27 @@ extension String { return "\(prefix(length))…" } } + +extension String { + func replacingHtmlBreaksOccurrences() -> String { + var result = self + let pattern = #"

(\n+)

"# + + guard let regex = try? NSRegularExpression(pattern: pattern, options: []) else { + return result + } + let matches = regex.matches(in: self, options: [], range: NSRange(location: 0, length: utf16.count)) + + for match in matches.reversed() { + guard let range = Range(match.range, in: self), + let innerMatchRange = Range(match.range(at: 1), in: self) else { + continue + } + let numberOfBreaks = (self[innerMatchRange].components(separatedBy: "\n").count - 1) + let replacement = "
" + String(repeating: "
", count: numberOfBreaks) + result.replaceSubrange(range, with: replacement) + } + + return result + } +} diff --git a/ElementX/Sources/Other/HTMLParsing/AttributedStringBuilder.swift b/ElementX/Sources/Other/HTMLParsing/AttributedStringBuilder.swift index 07192873b..943e1b364 100644 --- a/ElementX/Sources/Other/HTMLParsing/AttributedStringBuilder.swift +++ b/ElementX/Sources/Other/HTMLParsing/AttributedStringBuilder.swift @@ -73,14 +73,16 @@ struct AttributedStringBuilder: AttributedStringBuilderProtocol { // that could happen with the default HTML renderer of NSAttributedString which is a // webview. func fromHTML(_ htmlString: String?) -> AttributedString? { - guard let htmlString else { + guard var originalHTMLString = htmlString else { return nil } - if let cached = Self.caches[cacheKey]?.value(forKey: htmlString) { + if let cached = Self.caches[cacheKey]?.value(forKey: originalHTMLString) { return cached } + let htmlString = originalHTMLString.replacingHtmlBreaksOccurrences() + guard let data = htmlString.data(using: .utf8) else { return nil } diff --git a/ElementX/Sources/Screens/RoomScreen/View/Timeline/FormattedBodyText.swift b/ElementX/Sources/Screens/RoomScreen/View/Timeline/FormattedBodyText.swift index a1a48c99e..3464362cd 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/Timeline/FormattedBodyText.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/Timeline/FormattedBodyText.swift @@ -204,7 +204,8 @@ struct FormattedBodyText_Previews: PreviewProvider, TestablePreview { Hello world """, "

This is a list

\n\n", - "" + "", + "

test

\n

test

" ] let attributedStringBuilder = AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL, mentionBuilder: MentionBuilder()) diff --git a/UnitTests/Sources/StringTests.swift b/UnitTests/Sources/StringTests.swift index 809f21b1b..b6920fa51 100644 --- a/UnitTests/Sources/StringTests.swift +++ b/UnitTests/Sources/StringTests.swift @@ -88,4 +88,27 @@ class StringTests: XCTestCase { func testEllipsizeNotNeeded() { XCTAssertEqual("ellipsize".ellipsize(length: 15), "ellipsize") } + + func testReplaceBreakOccurrences() { + let input0 = "

" + let input1 = "

\n

" + let input2 = "

\n\n

" + let input3 = "

\n\n\n\n

" + let input4 = "

a

\n

b

" + let input5 = "empty" + + let expectedOutput0 = input0 + let expectedOutput1 = "

" + let expectedOutput2 = "


" + let expectedOutput3 = "




" + let expectedOutput4 = "

a

b

" + let expectedOutput5 = input5 + + XCTAssertEqual(input0.replacingHtmlBreaksOccurrences(), expectedOutput0) + XCTAssertEqual(input1.replacingHtmlBreaksOccurrences(), expectedOutput1) + XCTAssertEqual(input2.replacingHtmlBreaksOccurrences(), expectedOutput2) + XCTAssertEqual(input3.replacingHtmlBreaksOccurrences(), expectedOutput3) + XCTAssertEqual(input4.replacingHtmlBreaksOccurrences(), expectedOutput4) + XCTAssertEqual(input5.replacingHtmlBreaksOccurrences(), expectedOutput5) + } } diff --git a/UnitTests/__Snapshots__/PreviewTests/test_formattedBodyText.1.png b/UnitTests/__Snapshots__/PreviewTests/test_formattedBodyText.1.png index 4a37acb24..3ca67e193 100644 --- a/UnitTests/__Snapshots__/PreviewTests/test_formattedBodyText.1.png +++ b/UnitTests/__Snapshots__/PreviewTests/test_formattedBodyText.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8388baf6d6dff565d85ce4e75053f465a923e3e3216649e860f8103fe2bf2293 -size 212392 +oid sha256:afb864a0a68877a81dc46f01add0830bb6de243de11c70181624bd48b3886916 +size 212427