| ... | ... | @@ -76,7 +76,11 @@ import { |
| 76 | 76 | } from "../review/nativeReviewDiffAdapter"; |
| 77 | 77 | import { buildReviewParsedDiff } from "../review/reviewModel"; |
| 78 | 78 | import { cn } from "../../lib/cn"; |
| 79 | | import { deriveCenteredContentHorizontalPadding, type LayoutVariant } from "../../lib/layout"; |
| 79 | import { |
| 80 | deriveCenteredContentHorizontalPadding, |
| 81 | deriveThreadFeedInitialContentInset, |
| 82 | type LayoutVariant, |
| 83 | } from "../../lib/layout"; |
| 80 | 84 | import { |
| 81 | 85 | resolveMarkdownFontSizes, |
| 82 | 86 | resolveNativeMarkdownTypography, |
| ... | ... | @@ -1555,6 +1559,11 @@ export const ThreadFeed = memo(function ThreadFeed(props: ThreadFeedProps) { |
| 1555 | 1559 | const bottomContentInset = props.contentBottomInset ?? 18; |
| 1556 | 1560 | const usesNativeAutomaticInsets = |
| 1557 | 1561 | props.usesAutomaticContentInsets === true && Platform.OS === "ios"; |
| 1562 | const initialContentInset = deriveThreadFeedInitialContentInset({ |
| 1563 | platform: Platform.OS, |
| 1564 | usesNativeAutomaticInsets, |
| 1565 | bottomContentInset, |
| 1566 | }); |
| 1558 | 1567 | // With automatic insets the header inset lives in UIKit's adjustedContentInset, |
| 1559 | 1568 | // which LegendList's JS anchoring math cannot see — it measures the anchored |
| 1560 | 1569 | // end space from the scroll view's frame top. Fold the header height back into |
| ... | ... | @@ -1794,10 +1803,10 @@ export const ThreadFeed = memo(function ThreadFeed(props: ThreadFeedProps) { |
| 1794 | 1803 | // The empty↔filled key below remounts the list, which resets its imperative |
| 1795 | 1804 | // content-inset override — and useKeyboardChatComposerInset (mounted above |
| 1796 | 1805 | // the remount boundary) deduplicates by height, so it never re-reports the |
| 1797 | | // composer inset to the fresh instance. Without this, the remounted list's |
| 1798 | | // initial scroll-to-end computes with a zero end inset and rests one |
| 1799 | | // composer-height short of the end. Layout effect: it must land before the |
| 1800 | | // list's first positioning tick or the one-shot initial scroll misses it. |
| 1806 | // composer inset to the fresh instance. Re-report the measured overlay height |
| 1807 | // (composer plus any pending approval / user-input card) so the remounted |
| 1808 | // list's scroll math gets the true value; on Android the declarative |
| 1809 | // contentInset floor below covers the window before this effect lands. |
| 1801 | 1810 | const listMountKey = `${feedThreadKey}:${props.feed.length === 0 ? "empty" : "filled"}`; |
| 1802 | 1811 | useLayoutEffect(() => { |
| 1803 | 1812 | const bottom = props.contentInsetEndAdjustment.value; |
| ... | ... | @@ -2104,6 +2113,17 @@ export const ThreadFeed = memo(function ThreadFeed(props: ThreadFeedProps) { |
| 2104 | 2113 | // ThreadDetailScreen); this tells LegendList's scroll math about the |
| 2105 | 2114 | // extra so programmatic end scrolls land at the true resting offset. |
| 2106 | 2115 | contentInsetEndStaticAdjustment={usesNativeAutomaticInsets ? insets.bottom : 0} |
| 2116 | // Android: the composer overlay only exists as the keyboard |
| 2117 | // integration's animated bottom padding, which the list's scroll |
| 2118 | // math cannot see until the inset reports above land — and those |
| 2119 | // arrive via runOnJS, racing the remounted list's one-shot initial |
| 2120 | // scroll-at-end. Seed the estimated overlay height as a declarative |
| 2121 | // contentInset floor: LegendList consumes it in JS math only |
| 2122 | // (Android's ScrollView has no native contentInset prop) and the |
| 2123 | // first reported override REPLACES it instead of adding to it. |
| 2124 | // Not on iOS: there the prop would reach UIKit and inset natively |
| 2125 | // on top of the animated padding. |
| 2126 | {...(initialContentInset ? { contentInset: initialContentInset } : {})} |
| 2107 | 2127 | // The keyboard integration's offset math (end pinning, max scroll) |
| 2108 | 2128 | // must add the same UIKit-added extra, or its keyboard-open end |
| 2109 | 2129 | // targets land one safe-area short of the true resting offset. |