1
0
mirror of https://github.com/tooot-app/app synced 2025-04-25 15:38:42 +02:00

Fetching now works better

For #490
This commit is contained in:
xmflsct 2023-01-06 22:58:01 +01:00
parent c2aa78fef8
commit 70d57ed830
2 changed files with 131 additions and 147 deletions

View File

@ -4,9 +4,9 @@ import { InfiniteData, useQueryClient } from '@tanstack/react-query'
import { QueryKeyTimeline, TimelineData, useTimelineQuery } from '@utils/queryHooks/timeline' import { QueryKeyTimeline, TimelineData, useTimelineQuery } from '@utils/queryHooks/timeline'
import { StyleConstants } from '@utils/styles/constants' import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager' import { useTheme } from '@utils/styles/ThemeManager'
import React, { RefObject, useCallback, useRef, useState } from 'react' import React, { RefObject, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { FlatList, LayoutChangeEvent, Platform, StyleSheet, Text, View } from 'react-native' import { FlatList, Platform, Text, View } from 'react-native'
import { Circle } from 'react-native-animated-spinkit' import { Circle } from 'react-native-animated-spinkit'
import Animated, { import Animated, {
Extrapolate, Extrapolate,
@ -45,46 +45,47 @@ const TimelineRefresh: React.FC<Props> = ({
} }
const fetchingLatestIndex = useRef(0) const fetchingLatestIndex = useRef(0)
const FETCHING_LATEST_MAX = 5
const refetchActive = useRef(false) const refetchActive = useRef(false)
const { refetch, isFetching, isLoading, fetchPreviousPage, hasPreviousPage, isFetchingNextPage } = const { refetch, isFetching, fetchPreviousPage, hasPreviousPage } = useTimelineQuery({
useTimelineQuery({ ...queryKey[1],
...queryKey[1], options: {
options: { getPreviousPageParam: firstPage =>
getPreviousPageParam: firstPage => firstPage?.links?.prev && {
firstPage?.links?.prev && { ...(firstPage.links.prev.isOffset
...(firstPage.links.prev.isOffset ? { offset: firstPage.links.prev.id }
? { offset: firstPage.links.prev.id } : { min_id: firstPage.links.prev.id }),
: { min_id: firstPage.links.prev.id }), // https://github.com/facebook/react-native/issues/25239#issuecomment-731100372
// https://github.com/facebook/react-native/issues/25239#issuecomment-731100372 limit: '3'
limit: '3'
},
select: data => {
if (refetchActive.current) {
data.pageParams = [data.pageParams[0]]
data.pages = [data.pages[0]]
refetchActive.current = false
}
return data
}, },
onSuccess: () => { select: data => {
if (fetchingLatestIndex.current > 0) { if (refetchActive.current) {
if (fetchingLatestIndex.current > 5) { data.pageParams = [data.pageParams[0]]
data.pages = [data.pages[0]]
refetchActive.current = false
}
return data
},
onSuccess: async () => {
if (fetchingLatestIndex.current > 0) {
if (fetchingLatestIndex.current > FETCHING_LATEST_MAX) {
clearFirstPage()
fetchingLatestIndex.current = 0
} else {
if (hasPreviousPage) {
await new Promise(res => setTimeout(res, 100))
fetchPreviousPage()
fetchingLatestIndex.current++
} else {
clearFirstPage() clearFirstPage()
fetchingLatestIndex.current = 0 fetchingLatestIndex.current = 0
} else {
if (hasPreviousPage) {
fetchPreviousPage()
fetchingLatestIndex.current++
} else {
clearFirstPage()
fetchingLatestIndex.current = 0
}
} }
} }
} }
} }
}) }
})
const { t } = useTranslation('componentTimeline') const { t } = useTranslation('componentTimeline')
const { colors } = useTheme() const { colors } = useTheme()
@ -102,27 +103,6 @@ const TimelineRefresh: React.FC<Props> = ({
} }
}) })
} }
const prepareRefetch = () => {
refetchActive.current = true
queryClient.setQueryData<InfiniteData<TimelineData> | undefined>(queryKey, data => {
if (data) {
data.pageParams = [undefined]
const newFirstPage: TimelineData = { body: [] }
for (let page of data.pages) {
// @ts-ignore
newFirstPage.body.push(...page.body)
if (newFirstPage.body.length > 10) break
}
data.pages = [newFirstPage]
}
return data
})
}
const callRefetch = async () => {
await refetch()
setTimeout(() => flRef.current?.scrollToOffset({ offset: 1 }), 50)
}
const [textRight, setTextRight] = useState(0) const [textRight, setTextRight] = useState(0)
const arrowY = useAnimatedStyle(() => ({ const arrowY = useAnimatedStyle(() => ({
@ -145,14 +125,6 @@ const TimelineRefresh: React.FC<Props> = ({
})) }))
const arrowStage = useSharedValue(0) const arrowStage = useSharedValue(0)
const onLayout = useCallback(
({ nativeEvent }: LayoutChangeEvent) => {
if (nativeEvent.layout.x + nativeEvent.layout.width > textRight) {
setTextRight(nativeEvent.layout.x + nativeEvent.layout.width)
}
},
[textRight]
)
useAnimatedReaction( useAnimatedReaction(
() => { () => {
if (isFetching) { if (isFetching) {
@ -190,8 +162,32 @@ const TimelineRefresh: React.FC<Props> = ({
}, },
[isFetching] [isFetching]
) )
const wrapperStartLatest = () => {
const runFetchPrevious = () => {
fetchingLatestIndex.current = 1 fetchingLatestIndex.current = 1
fetchPreviousPage()
}
const prepareRefetch = () => {
refetchActive.current = true
queryClient.setQueryData<InfiniteData<TimelineData> | undefined>(queryKey, data => {
if (data) {
data.pageParams = [undefined]
const newFirstPage: TimelineData = { body: [] }
for (let page of data.pages) {
// @ts-ignore
newFirstPage.body.push(...page.body)
if (newFirstPage.body.length > 10) break
}
data.pages = [newFirstPage]
}
return data
})
}
const callRefetch = async () => {
await refetch()
setTimeout(() => flRef.current?.scrollToOffset({ offset: 0 }), 50)
} }
useAnimatedReaction( useAnimatedReaction(
@ -202,95 +198,85 @@ const TimelineRefresh: React.FC<Props> = ({
fetchingType.value = 0 fetchingType.value = 0
switch (data) { switch (data) {
case 1: case 1:
runOnJS(wrapperStartLatest)() runOnJS(runFetchPrevious)()
runOnJS(clearFirstPage)() return
runOnJS(fetchPreviousPage)()
break
case 2: case 2:
runOnJS(prepareRefetch)() runOnJS(prepareRefetch)()
runOnJS(callRefetch)() runOnJS(callRefetch)()
break return
} }
}, },
[] []
) )
const headerPadding = useAnimatedStyle(
() => ({
paddingTop:
fetchingLatestIndex.current !== 0 || (isFetching && !isLoading && !isFetchingNextPage)
? withTiming(StyleConstants.Spacing.M * 2.5)
: withTiming(0)
}),
[fetchingLatestIndex.current, isFetching, isFetchingNextPage, isLoading]
)
return ( return (
<Animated.View style={headerPadding}> <Animated.View
<View style={styles.base}> style={{
{isFetching ? ( position: 'absolute',
<View style={styles.container2}> top: 0,
<Circle size={StyleConstants.Font.Size.L} color={colors.secondary} /> left: 0,
</View> right: 0,
) : ( height: CONTAINER_HEIGHT * 2,
<> alignItems: 'center'
<View style={styles.container1}> }}
<Text >
style={[styles.explanation, { color: colors.primaryDefault }]} {(fetchingLatestIndex.current || 99) <= FETCHING_LATEST_MAX || refetchActive.current ? (
onLayout={onLayout} <View style={{ height: CONTAINER_HEIGHT, justifyContent: 'center' }}>
children={t('refresh.fetchPreviousPage')} <Circle size={StyleConstants.Font.Size.L} color={colors.secondary} />
/> </View>
<Animated.View ) : (
style={[ <>
{ <View style={{ flex: 1, flexDirection: 'row', height: CONTAINER_HEIGHT }}>
position: 'absolute', <Text
left: textRight + StyleConstants.Spacing.S style={{
}, fontSize: StyleConstants.Font.Size.S,
arrowY, lineHeight: CONTAINER_HEIGHT,
arrowTop color: colors.primaryDefault
]} }}
children={ onLayout={({ nativeEvent }) => {
<Icon if (nativeEvent.layout.x + nativeEvent.layout.width > textRight) {
name='ArrowLeft' setTextRight(nativeEvent.layout.x + nativeEvent.layout.width)
size={StyleConstants.Font.Size.M}
color={colors.primaryDefault}
/>
} }
/> }}
</View> children={t('refresh.fetchPreviousPage')}
<View style={styles.container2}> />
<Text <Animated.View
style={[styles.explanation, { color: colors.primaryDefault }]} style={[
onLayout={onLayout} {
children={t('refresh.refetch')} position: 'absolute',
/> left: textRight + StyleConstants.Spacing.S
</View> },
</> arrowY,
)} arrowTop
</View> ]}
children={
<Icon
name='ArrowLeft'
size={StyleConstants.Font.Size.M}
color={colors.primaryDefault}
/>
}
/>
</View>
<View style={{ height: CONTAINER_HEIGHT, justifyContent: 'center' }}>
<Text
style={{
fontSize: StyleConstants.Font.Size.S,
lineHeight: CONTAINER_HEIGHT,
color: colors.primaryDefault
}}
onLayout={({ nativeEvent }) => {
if (nativeEvent.layout.x + nativeEvent.layout.width > textRight) {
setTextRight(nativeEvent.layout.x + nativeEvent.layout.width)
}
}}
children={t('refresh.refetch')}
/>
</View>
</>
)}
</Animated.View> </Animated.View>
) )
} }
const styles = StyleSheet.create({
base: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
height: CONTAINER_HEIGHT * 2,
alignItems: 'center'
},
container1: {
flex: 1,
flexDirection: 'row',
height: CONTAINER_HEIGHT
},
container2: { height: CONTAINER_HEIGHT, justifyContent: 'center' },
explanation: {
fontSize: StyleConstants.Font.Size.S,
lineHeight: CONTAINER_HEIGHT
}
})
export default TimelineRefresh export default TimelineRefresh

View File

@ -129,13 +129,11 @@ const Timeline: React.FC<Props> = ({
/> />
) )
} }
maintainVisibleContentPosition={ {...(!isLoading && {
isFetching maintainVisibleContentPosition: {
? { minIndexForVisible: 0
minIndexForVisible: 0 }
} })}
: undefined
}
{...androidRefreshControl} {...androidRefreshControl}
{...customProps} {...customProps}
/> />