From 8f4f46326586761db07700489e3df9da4ffefd54 Mon Sep 17 00:00:00 2001 From: isaac <> Date: Thu, 28 May 2026 18:17:02 +0200 Subject: [PATCH] Fix layout --- .../InstantPageUI/Sources/InstantPageV2Layout.swift | 11 ++++++++++- .../ChatMessageRichDataBubbleContentNode.swift | 10 +++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/submodules/InstantPageUI/Sources/InstantPageV2Layout.swift b/submodules/InstantPageUI/Sources/InstantPageV2Layout.swift index 5d548a2b80..dfb798709e 100644 --- a/submodules/InstantPageUI/Sources/InstantPageV2Layout.swift +++ b/submodules/InstantPageUI/Sources/InstantPageV2Layout.swift @@ -437,7 +437,16 @@ public func lastTextLineFrameIfLastItemIsText(in layout: InstantPageV2Layout) -> else { return nil } - let lineFrame = last.frame.offsetBy(dx: text.frame.minX, dy: text.frame.minY) + // The stored line frame always has minX = 0 — alignment (center / right / natural-RTL) is + // applied at render time by `v2FrameForLine`. Apply the same correction here so the returned + // frame's `maxX` reflects the line's actual on-screen right edge, not just its width anchored + // at the textItem's left. Without this, a right-aligned or RTL last line — whose visible right + // edge sits at `textItem.width`, all the way at the right text inset — would feed the status + // node a `contentWidth` equal to just `lineWidth`. The trail/wrap decision would then think + // the date fits trailing the line, and place it directly on top of the line at the right text + // inset where the line itself ends. The width is unchanged; only `origin.x` shifts. + let displayedLineFrame = v2FrameForLine(last, boundingWidth: text.textItem.frame.width, alignment: text.textItem.alignment) + let lineFrame = displayedLineFrame.offsetBy(dx: text.frame.minX, dy: text.frame.minY) var ascent: CGFloat = 0.0 var descent: CGFloat = 0.0 var leading: CGFloat = 0.0 diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageRichDataBubbleContentNode/Sources/ChatMessageRichDataBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageRichDataBubbleContentNode/Sources/ChatMessageRichDataBubbleContentNode.swift index bfc42cdbe2..f2640d0146 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageRichDataBubbleContentNode/Sources/ChatMessageRichDataBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageRichDataBubbleContentNode/Sources/ChatMessageRichDataBubbleContentNode.swift @@ -576,7 +576,15 @@ public class ChatMessageRichDataBubbleContentNode: ChatMessageBubbleContentNode isReplyThread = true } - let trailingWidthToMeasure: CGFloat = lastTextLineFrame?.width ?? 10000.0 + // Measure trailing extent from the line's actual visible RIGHT EDGE (after + // alignment, in page coords) — not just its intrinsic width. A right-aligned + // or RTL last line has `lineWidth` worth of glyphs but sits all the way at + // the right text inset (lineFrame.maxX == text.frame.minX + textItem.width). + // Feeding the status node just `lineWidth` would let the trail/wrap decision + // place the date inline with the line — on top of it. `pageHorizontalInset` + // is the offset between page-coords and status-node-local coords (the status + // node sits at x=pageHorizontalInset in self, and pageView sits at self-x 0). + let trailingWidthToMeasure: CGFloat = lastTextLineFrame.map { $0.maxX - pageHorizontalInset } ?? 10000.0 let dateLayoutInput: ChatMessageDateAndStatusNode.LayoutInput = .trailingContent(contentWidth: trailingWidthToMeasure, reactionSettings: ChatMessageDateAndStatusNode.TrailingReactionSettings(displayInline: shouldDisplayInlineDateReactions(message: EngineMessage(item.message), isPremium: item.associatedData.isPremium, forceInline: item.associatedData.forceInlineReactions), preferAdditionalInset: false))