mostly working

This commit is contained in:
Hailey
2024-04-25 12:03:13 -07:00
parent 1155351224
commit dce3d89357
2 changed files with 104 additions and 99 deletions
+103 -53
View File
@@ -1,17 +1,15 @@
import React from 'react'
import {
FlatList,
LayoutChangeEvent,
ListRenderItemInfo,
View,
ViewToken,
} from 'react-native'
import {Button, FlatList, View, ViewToken} from 'react-native'
import {atoms as a, useTheme} from '#/alf'
import {ClipClop as ClipClopInterface} from '#/components/ClipClopTest/RandomClipClops'
import {useTheme} from '#/alf'
import {atoms as a} from '#/alf'
import {
ClipClop,
placeholderClops,
} from '#/components/ClipClopTest/RandomClipClops'
import {Text} from '#/components/Typography'
function ClipClop({message}: {message: ClipClopInterface}) {
function ClipClopItem({item}: {item: ClipClop}) {
const t = useTheme()
return (
<View
@@ -25,57 +23,80 @@ function ClipClop({message}: {message: ClipClopInterface}) {
borderRadius: 10,
},
]}>
<Text style={{lineHeight: 1.2}}>{message.text}</Text>
<Text style={{lineHeight: 1.2}}>{item.text}</Text>
</View>
)
}
function renderItem({item}: ListRenderItemInfo<ClipClopInterface>) {
return <ClipClop message={item} />
function renderItem({item}: {item: ClipClop}) {
return <ClipClopItem item={item} />
}
function keyExtractor(item: ClipClopInterface, index: number) {
return `${item.id}-${index}`
}
// Generate unique key list item.
export const generateUniqueKey = () =>
`_${Math.random().toString(36).substr(2, 9)}`
function HeaderPlaceholder({height}: {height: number}) {
return <View style={{height}} />
}
export const ClipClopList = () => {
const flatListRef = React.useRef<FlatList>(null)
export function ClipClopList({clipClops}: {clipClops: ClipClopInterface[]}) {
const flatlistRef = React.useRef<FlatList>(null)
const contentHeight = React.useRef(0)
// Whenever we reach the end (visually the top), we don't want to keep calling it. We will set `isFetching` to true
// once the request for new posts starts. Then, we will change it back to false after the content size changes.
const isFetching = React.useRef(false)
const totalClipClops = React.useRef(0)
// We use this to know if we should scroll after a new clop is added to the list
const isAtBottom = React.useRef(false)
const isAtBottom = React.useRef(true)
const [placeholderHeight, setPlaceholderHeight] = React.useState(0)
// Because the viewableItemsChanged callback won't have access to the updated state, we use a ref to store the
// total number of clops
const totalClops = React.useRef(10)
// This creates a placeholder that adjusts with the whitespace above the last chat item
const onScreenLayout = React.useCallback((e: LayoutChangeEvent) => {
setPlaceholderHeight(e.nativeEvent.layout.height)
}, [])
const onContentSizeChange = React.useCallback(
(_: number, height: number) => {
if (isAtBottom.current) {
flatlistRef.current?.scrollToOffset({offset: height, animated: true})
}
contentHeight.current = height
totalClipClops.current = clipClops.length
},
[clipClops],
const [clops, setClops] = React.useState(
Array.from(Array(10).keys()).map(n => ({
id: generateUniqueKey(),
text: placeholderClops[n % placeholderClops.length],
})),
)
const addOldClops = () => {
setClops(prev => {
const oldClops: ClipClop[] = Array.from(Array(5).keys()).map(n => ({
id: generateUniqueKey(),
text:
placeholderClops[n % placeholderClops.length] + generateUniqueKey(),
}))
totalClops.current += oldClops.length
return prev.concat(oldClops)
})
}
const addNewClip = () => {
setClops(prev => {
const newClops = Array.from(Array(1).keys())
.map(n => ({
id: generateUniqueKey(),
text:
placeholderClops[n % placeholderClops.length] + generateUniqueKey(),
}))
.reverse()
totalClops.current += newClops.length
return newClops.concat(prev)
})
}
const [onViewableItemsChanged, viewabilityConfig] = React.useMemo(() => {
return [
(info: {viewableItems: Array<ViewToken>; changed: Array<ViewToken>}) => {
const lastVisibleItem =
info.viewableItems[info.viewableItems.length - 1]
const lastViewableIndex = lastVisibleItem?.index ?? -1
const firstVisibleIndex = info.viewableItems[0]?.index
isAtBottom.current = lastViewableIndex >= totalClipClops.current - 2
isAtBottom.current = Number(firstVisibleIndex) < 2
console.log({
firstVisibleIndex,
totalClops: totalClops.current,
isAtBottom: isAtBottom.current,
})
},
{
itemVisiblePercentThreshold: 100,
@@ -84,21 +105,50 @@ export function ClipClopList({clipClops}: {clipClops: ClipClopInterface[]}) {
]
}, [])
const onContentSizeChange = React.useCallback(
(_: number, height: number) => {
if (isAtBottom.current) {
flatListRef.current?.scrollToOffset({offset: 0, animated: true})
}
isFetching.current = false
},
[clops],
)
const onEndReached = () => {
if (isFetching.current) return
isFetching.current = true
// We wouldn't actually use a timeout, but there would be a delay while loading
setTimeout(() => {
addOldClops()
}, 1000)
}
return (
<View style={{flex: 1}} onLayout={onScreenLayout}>
<View style={{flex: 1, marginBottom: 100}}>
<Button title="Add Old Clops" onPress={addOldClops} />
<FlatList
data={clipClops}
data={clops}
keyExtractor={item => item.id}
maintainVisibleContentPosition={{
minIndexForVisible: 1,
}}
renderItem={renderItem}
keyExtractor={keyExtractor}
// maintainVisibleContentPosition={{
// minIndexForVisible: 0,
// }}
// ListHeaderComponent={<HeaderPlaceholder height={placeholderHeight} />}
onContentSizeChange={onContentSizeChange}
onViewableItemsChanged={onViewableItemsChanged}
viewabilityConfig={viewabilityConfig}
ref={flatlistRef}
onContentSizeChange={onContentSizeChange}
ref={flatListRef}
onScrollToIndexFailed={info => {
console.log('Failed to scroll to index', info)
}}
initialNumToRender={20}
maxToRenderPerBatch={20}
inverted
onEndReached={onEndReached}
/>
<Button title="Add New Clip" onPress={addNewClip} />
</View>
)
}
+1 -46
View File
@@ -51,52 +51,7 @@ type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home'>
*/
export function HomeScreen(props: Props) {
const lastMessageId = React.useRef(0)
const [clipClops, setClipClops] = React.useState<ClipClop[]>([])
const addMessage = () => {
lastMessageId.current += 1
setClipClops(prev => {
return [
...prev,
{
id: lastMessageId.current.toString(),
text: `${
placeholderClops[lastMessageId.current % placeholderClops.length]
}`,
},
]
})
}
const addMessages = (count: number) => {
for (let i = 0; i < count; i++) {
addMessage()
}
}
return (
<CenteredView style={{flex: 1, marginBottom: 100}}>
<View style={{flexDirection: 'row'}}>
<Button
title="Add One"
onPress={() => {
addMessages(1)
}}
/>
<Button
title="Add Five"
onPress={() => {
addMessages(5)
}}
/>
<Button title="Reset" onPress={() => setClipClops([])} />
</View>
<ClipClopList clipClops={clipClops} />
</CenteredView>
)
return <ClipClopList />
}
function HomeScreenReady({