From b65e8f6a7c381c9c31542040a939f289be164e59 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Thu, 1 Oct 2026 13:41:13 +0200 Subject: [PATCH] Fix formatting-only edits not rendering in the timeline Components of a formatted body were identified by their plain characters, so an edit that only added formatting (e.g. wrapping a word in inline code) kept the same ID. SwiftUI then reused the existing MessageText, whose @State string ignores new input, and the old unformatted text kept rendering. Identify components by the hash of their attributed string so formatting changes produce a new ID. --- ElementX/Sources/Other/Extensions/AttributedString.swift | 3 ++- UnitTests/Sources/AttributedStringBuilderTests.swift | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/ElementX/Sources/Other/Extensions/AttributedString.swift b/ElementX/Sources/Other/Extensions/AttributedString.swift index d82baa26b..2c935842c 100644 --- a/ElementX/Sources/Other/Extensions/AttributedString.swift +++ b/ElementX/Sources/Other/Extensions/AttributedString.swift @@ -40,7 +40,8 @@ nonisolated extension AttributedString { .plainText } - components.append(AttributedStringBuilderComponent(id: String(attributedString.characters), + // Hash attributes as well as characters so formatting-only edits change the ID and refresh the view. + components.append(AttributedStringBuilderComponent(id: String(attributedString.hashValue), attributedString: attributedString, type: componentType)) } diff --git a/UnitTests/Sources/AttributedStringBuilderTests.swift b/UnitTests/Sources/AttributedStringBuilderTests.swift index b33c44cfc..330dd5852 100644 --- a/UnitTests/Sources/AttributedStringBuilderTests.swift +++ b/UnitTests/Sources/AttributedStringBuilderTests.swift @@ -257,6 +257,15 @@ struct AttributedStringBuilderTests { #expect(components[1].attributedString.string == "Hidden content") } + @Test + func componentIDChangesWithFormatting() throws { + let plain = try #require(attributedStringBuilder.fromHTML("test test test"), "Could not build the attributed string") + let formatted = try #require(attributedStringBuilder.fromHTML("test test test"), "Could not build the attributed string") + + #expect(plain.string == formatted.string) + #expect(plain.formattedComponents.map(\.id) != formatted.formattedComponents.map(\.id)) + } + @Test func detailsWithoutSummary() throws { let htmlString = "

Content

"