mostly working
This commit is contained in:
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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({
|
||||
|
||||
Reference in New Issue
Block a user