diff --git a/.github/pr-screenshots/2345-header-probe/inverted-clear-dark.png b/.github/pr-screenshots/2345-header-probe/inverted-clear-dark.png new file mode 100644 index 0000000000..fa2d8ff86c Binary files /dev/null and b/.github/pr-screenshots/2345-header-probe/inverted-clear-dark.png differ diff --git a/.github/pr-screenshots/2345-header-probe/inverted-clear-light.png b/.github/pr-screenshots/2345-header-probe/inverted-clear-light.png new file mode 100644 index 0000000000..ea2ed6e957 Binary files /dev/null and b/.github/pr-screenshots/2345-header-probe/inverted-clear-light.png differ diff --git a/.github/pr-screenshots/2345-header-probe/inverted-undefined-dark.png b/.github/pr-screenshots/2345-header-probe/inverted-undefined-dark.png new file mode 100644 index 0000000000..fa2d8ff86c Binary files /dev/null and b/.github/pr-screenshots/2345-header-probe/inverted-undefined-dark.png differ diff --git a/.github/pr-screenshots/2345-header-probe/inverted-undefined-light.png b/.github/pr-screenshots/2345-header-probe/inverted-undefined-light.png new file mode 100644 index 0000000000..ea2ed6e957 Binary files /dev/null and b/.github/pr-screenshots/2345-header-probe/inverted-undefined-light.png differ diff --git a/.github/pr-screenshots/2345-header-probe/normal-clear-dark.png b/.github/pr-screenshots/2345-header-probe/normal-clear-dark.png new file mode 100644 index 0000000000..cd5b970cf8 Binary files /dev/null and b/.github/pr-screenshots/2345-header-probe/normal-clear-dark.png differ diff --git a/.github/pr-screenshots/2345-header-probe/normal-clear-light.png b/.github/pr-screenshots/2345-header-probe/normal-clear-light.png new file mode 100644 index 0000000000..30435b2ff6 Binary files /dev/null and b/.github/pr-screenshots/2345-header-probe/normal-clear-light.png differ diff --git a/.github/pr-screenshots/2345-header-probe/normal-undefined-dark.png b/.github/pr-screenshots/2345-header-probe/normal-undefined-dark.png new file mode 100644 index 0000000000..2914657db4 Binary files /dev/null and b/.github/pr-screenshots/2345-header-probe/normal-undefined-dark.png differ diff --git a/.github/pr-screenshots/2345-header-probe/normal-undefined-light.png b/.github/pr-screenshots/2345-header-probe/normal-undefined-light.png new file mode 100644 index 0000000000..87ecf2baa0 Binary files /dev/null and b/.github/pr-screenshots/2345-header-probe/normal-undefined-light.png differ diff --git a/apps/mobile/app/dev/header-probe.tsx b/apps/mobile/app/dev/header-probe.tsx new file mode 100644 index 0000000000..44ca33ca54 --- /dev/null +++ b/apps/mobile/app/dev/header-probe.tsx @@ -0,0 +1,135 @@ +import { Stack, useLocalSearchParams, useRouter } from 'expo-router'; +import { useRef } from 'react'; +import { FlatList, Pressable, View } from 'react-native'; + +import { useDesignTheme } from '../../src/ui/design/theme'; +import { radius, space } from '../../src/ui/design/tokens'; +import { Text } from '../../src/ui/primitives/text'; + +type Direction = 'normal' | 'inverted'; +type HeaderFill = 'undefined' | 'clear'; + +const LINES = [ + 'Short one.', + 'A longer reply that wraps onto a second line so the bubble has some height to it.', + 'Scroll me under the header and watch the top edge.', + 'Three\nshort\nlines.', + 'The question is whether the whole list goes darker once the header is transparent.', +]; + +const MESSAGES = Array.from({ length: 40 }, (_, index) => ({ + key: String(index), + mine: index % 3 === 1, + text: `${String(index + 1)}. ${LINES[index % LINES.length] ?? ''}`, +})); + +export default function HeaderProbe() { + const theme = useDesignTheme(); + const router = useRouter(); + const params = useLocalSearchParams<{ list?: string; fill?: string }>(); + const direction: Direction = params.list === 'inverted' ? 'inverted' : 'normal'; + const fill: HeaderFill = params.fill === 'clear' ? 'clear' : 'undefined'; + const list = useRef>(null); + const settled = useRef(false); + const inverted = direction === 'inverted'; + const data = inverted ? [...MESSAGES].reverse() : MESSAGES; + + const option = (label: string, active: boolean, onPress: () => void) => ( + + + {label} + + + ); + + const controls = ( + + + {option('Normal list', !inverted, () => { + settled.current = false; + router.setParams({ list: 'normal' }); + })} + {option('Inverted list', inverted, () => { + settled.current = false; + router.setParams({ list: 'inverted' }); + })} + + + {option('Header bg undefined', fill === 'undefined', () => + router.setParams({ fill: 'undefined' }), + )} + {option('Header bg clear', fill === 'clear', () => router.setParams({ fill: 'clear' }))} + + + ); + + return ( + <> + + item.key} + contentInsetAdjustmentBehavior="automatic" + automaticallyAdjustsScrollIndicatorInsets + style={{ flex: 1, backgroundColor: theme.color.base }} + contentContainerStyle={{ paddingVertical: space.x3, gap: space.x2 }} + onContentSizeChange={() => { + if (inverted || settled.current) return; + settled.current = true; + list.current?.scrollToEnd({ animated: false }); + }} + {...(inverted ? { ListFooterComponent: controls } : { ListHeaderComponent: controls })} + renderItem={({ item }) => ( + + + {item.text} + + + )} + /> + + ); +}