InstantPage V2: support nested <details> 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) <noreply@anthropic.com>
This commit is contained in:
parent
a4cddb74d6
commit
0d5b051820
2 changed files with 22 additions and 4 deletions
|
|
@ -1803,6 +1803,13 @@ final class InstantPageV2DetailsView: UIView, InstantPageItemView {
|
||||||
self.addSubview(body)
|
self.addSubview(body)
|
||||||
self.bodyView = 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.update(layout: innerLayout, theme: theme, animation: animation)
|
||||||
body.frame = CGRect(
|
body.frame = CGRect(
|
||||||
origin: CGPoint(x: 0.0, y: item.titleFrame.maxY),
|
origin: CGPoint(x: 0.0, y: item.titleFrame.maxY),
|
||||||
|
|
|
||||||
|
|
@ -164,12 +164,23 @@ public class ChatMessageRichDataBubbleContentNode: ChatMessageBubbleContentNode
|
||||||
|
|
||||||
private func defaultExpanded(forDetailsIndex index: Int) -> Bool {
|
private func defaultExpanded(forDetailsIndex index: Int) -> Bool {
|
||||||
guard let layout = self.currentPageLayout?.layout else { return false }
|
guard let layout = self.currentPageLayout?.layout else { return false }
|
||||||
for item in layout.items {
|
func search(_ items: [InstantPageV2LaidOutItem]) -> Bool? {
|
||||||
if case let .details(d) = item, d.index == index {
|
for item in items {
|
||||||
return d.defaultExpanded
|
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) {
|
required public init?(coder aDecoder: NSCoder) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue