From 0d5b051820fc7de5e7f2a6bc967b5d932d8f0e63 Mon Sep 17 00:00:00 2001 From: isaac <> Date: Thu, 4 Jun 2026 15:30:28 +0200 Subject: [PATCH] InstantPage V2: support nested
blocks A details block nested inside another rendered (title + chevron) but could not expand: its body is hosted by a fresh InstantPageV2View whose detailsTapped closure was never wired (only the top-level view's is, by the bubble), so the nested title's tap hit a nil closure and was dropped. Wire the body view's detailsTapped to chain through this view's onTitleTapped, which makeItemView already routes to the owning view's detailsTapped -> the bubble's toggle handler; this chains up through arbitrary nesting depth. Also make the bubble's defaultExpanded(forDetailsIndex:) recurse into expanded bodies so a nested details whose model default is expanded toggles from the correct state (the flat top-level scan missed nested indices). Known pre-existing limitation (unchanged): V2 keys expansion state by a flat DFS index, so expanding a nested details can shift sibling indices. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../Sources/InstantPageRenderer.swift | 7 +++++++ ...ChatMessageRichDataBubbleContentNode.swift | 19 +++++++++++++++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/submodules/InstantPageUI/Sources/InstantPageRenderer.swift b/submodules/InstantPageUI/Sources/InstantPageRenderer.swift index b0400327d5..23d8d00a07 100644 --- a/submodules/InstantPageUI/Sources/InstantPageRenderer.swift +++ b/submodules/InstantPageUI/Sources/InstantPageRenderer.swift @@ -1803,6 +1803,13 @@ final class InstantPageV2DetailsView: UIView, InstantPageItemView { self.addSubview(body) self.bodyView = body } + // Forward taps on details NESTED inside this body up to the same toggle handler this + // view uses: makeItemView wired our onTitleTapped to the owning InstantPageV2View's + // detailsTapped, so chaining through onTitleTapped reaches the bubble's toggle handler. + // Without this, a nested details' tap hits the body view's nil detailsTapped and is dropped. + body.detailsTapped = { [weak self] index in + self?.onTitleTapped?(index) + } body.update(layout: innerLayout, theme: theme, animation: animation) body.frame = CGRect( origin: CGPoint(x: 0.0, y: item.titleFrame.maxY), diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageRichDataBubbleContentNode/Sources/ChatMessageRichDataBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageRichDataBubbleContentNode/Sources/ChatMessageRichDataBubbleContentNode.swift index 468f35c942..4a562f581e 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageRichDataBubbleContentNode/Sources/ChatMessageRichDataBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageRichDataBubbleContentNode/Sources/ChatMessageRichDataBubbleContentNode.swift @@ -164,12 +164,23 @@ public class ChatMessageRichDataBubbleContentNode: ChatMessageBubbleContentNode private func defaultExpanded(forDetailsIndex index: Int) -> Bool { guard let layout = self.currentPageLayout?.layout else { return false } - for item in layout.items { - if case let .details(d) = item, d.index == index { - return d.defaultExpanded + func search(_ items: [InstantPageV2LaidOutItem]) -> Bool? { + for item in items { + if case let .details(d) = item { + if d.index == index { + return d.defaultExpanded + } + // Recurse into an expanded parent's body so NESTED details indices resolve too; + // the flat top-level scan missed them, leaving the toggle's "current state" + // computation wrong for a nested details whose model default is expanded. + if let inner = d.innerLayout, let found = search(inner.items) { + return found + } + } } + return nil } - return false + return search(layout.items) ?? false } required public init?(coder aDecoder: NSCoder) {