1
0
mirror of https://github.com/tooot-app/app synced 2025-04-23 22:57:23 +02:00

Retry refresh

This commit is contained in:
Zhiyuan Zheng 2020-12-21 22:58:53 +01:00
parent 1ee7dd920d
commit c47d4cb4c4
No known key found for this signature in database
GPG Key ID: 078A93AB607D85E0
9 changed files with 80 additions and 106 deletions

View File

@ -50,14 +50,12 @@ const Timeline: React.FC<Props> = ({
status, status,
data, data,
refetch, refetch,
isFetching,
isSuccess, isSuccess,
hasPreviousPage, hasPreviousPage,
fetchPreviousPage, fetchPreviousPage,
isFetchingPreviousPage, isFetchingPreviousPage,
hasNextPage, hasNextPage,
fetchNextPage, fetchNextPage
isFetchingNextPage
} = useInfiniteQuery(queryKey, timelineFetch, { } = useInfiniteQuery(queryKey, timelineFetch, {
getPreviousPageParam: firstPage => { getPreviousPageParam: firstPage => {
return firstPage.toots.length return firstPage.toots.length
@ -67,7 +65,7 @@ const Timeline: React.FC<Props> = ({
} }
: undefined : undefined
}, },
getNextPageParam: (lastPage, all) => { getNextPageParam: lastPage => {
return lastPage.toots.length return lastPage.toots.length
? { ? {
direction: 'next', direction: 'next',
@ -96,8 +94,8 @@ const Timeline: React.FC<Props> = ({
} }
}, [isSuccess]) }, [isSuccess])
const flKeyExtrator = useCallback(({ id }) => id, []) const keyExtractor = useCallback(({ id }) => id, [])
const flRenderItem = useCallback( const renderItem = useCallback(
({ item, index }) => { ({ item, index }) => {
switch (page) { switch (page) {
case 'Conversations': case 'Conversations':
@ -123,7 +121,7 @@ const Timeline: React.FC<Props> = ({
}, },
[flattenPinnedLength[0]] [flattenPinnedLength[0]]
) )
const flItemSeparatorComponent = useCallback( const ItemSeparatorComponent = useCallback(
({ leadingItem }) => ( ({ leadingItem }) => (
<TimelineSeparator <TimelineSeparator
{...(toot && toot.id === leadingItem.id && { highlighted: true })} {...(toot && toot.id === leadingItem.id && { highlighted: true })}
@ -135,27 +133,19 @@ const Timeline: React.FC<Props> = ({
() => <TimelineEmpty status={status} refetch={refetch} />, () => <TimelineEmpty status={status} refetch={refetch} />,
[status] [status]
) )
const flOnRefresh = useCallback(() => { const onEndReached = useCallback(() => fetchNextPage(), [])
!disableRefresh && const ListFooterComponent = useCallback(
(hasPreviousPage () => <TimelineEnd hasNextPage={hasNextPage} />,
? fetchPreviousPage()
: !isFetching
? refetch()
: undefined)
}, [hasPreviousPage, isFetching])
const flOnEndReach = useCallback(() => !disableRefresh && fetchNextPage(), [])
const flFooter = useCallback(
() => (!disableRefresh ? <TimelineEnd hasNextPage={hasNextPage} /> : null),
[hasNextPage] [hasNextPage]
) )
const flRefreshControl = useMemo( const refreshControl = useMemo(
() => ( () => (
<RefreshControl <RefreshControl
refreshing={isFetchingPreviousPage || isFetching} refreshing={isFetchingPreviousPage}
onRefresh={flOnRefresh} onRefresh={() => fetchPreviousPage()}
/> />
), ),
[isFetchingPreviousPage, isFetching] [isFetchingPreviousPage]
) )
const onScrollToIndexFailed = useCallback(error => { const onScrollToIndexFailed = useCallback(error => {
const offset = error.averageItemLength * error.index const offset = error.averageItemLength * error.index
@ -177,14 +167,14 @@ const Timeline: React.FC<Props> = ({
initialNumToRender={5} initialNumToRender={5}
maxToRenderPerBatch={5} maxToRenderPerBatch={5}
style={styles.flatList} style={styles.flatList}
renderItem={flRenderItem} renderItem={renderItem}
onEndReached={flOnEndReach} onEndReached={onEndReached}
keyExtractor={flKeyExtrator} keyExtractor={keyExtractor}
ListFooterComponent={flFooter} onEndReachedThreshold={0.75}
refreshControl={flRefreshControl} ListFooterComponent={ListFooterComponent}
ListEmptyComponent={flItemEmptyComponent} ListEmptyComponent={flItemEmptyComponent}
ItemSeparatorComponent={flItemSeparatorComponent} {...(!disableRefresh && { refreshControl })}
onEndReachedThreshold={!disableRefresh ? 0.75 : null} ItemSeparatorComponent={ItemSeparatorComponent}
{...(toot && isSuccess && { onScrollToIndexFailed })} {...(toot && isSuccess && { onScrollToIndexFailed })}
/> />
) )

View File

@ -17,7 +17,7 @@ const TimelineAvatar: React.FC<Props> = ({ queryKey, account }) => {
return ( return (
<Pressable style={styles.avatar} onPress={onPress}> <Pressable style={styles.avatar} onPress={onPress}>
<Image source={{ uri: account.avatar }} style={styles.image} /> <Image source={{ uri: account.avatar_static }} style={styles.image} />
</Pressable> </Pressable>
) )
} }

View File

@ -1,5 +1,5 @@
import React, { useRef, useState } from 'react' import React, { useRef, useState } from 'react'
import { Animated, ScrollView } from 'react-native' import { Animated, LayoutAnimation, ScrollView } from 'react-native'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
import { getLocalUrl } from '@utils/slices/instancesSlice' import { getLocalUrl } from '@utils/slices/instancesSlice'
@ -14,6 +14,7 @@ import { AccountState } from '../Shared/Account'
import AccountNav from '../Shared/Account/Nav' import AccountNav from '../Shared/Account/Nav'
const ScreenMeRoot: React.FC = () => { const ScreenMeRoot: React.FC = () => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
const localRegistered = useSelector(getLocalUrl) const localRegistered = useSelector(getLocalUrl)
const scrollRef = useRef<ScrollView>(null) const scrollRef = useRef<ScrollView>(null)

View File

@ -28,7 +28,7 @@ const MyInfo: React.FC<Props> = ({ setData }) => {
account={data} account={data}
limitHeight limitHeight
/> />
<AccountInformation account={data} /> <AccountInformation account={data} disableActions />
</> </>
) )
} }

View File

@ -1,5 +1,5 @@
import React, { useEffect, useReducer, useRef, useState } from 'react' import React, { useEffect, useReducer, useRef, useState } from 'react'
import { Animated, ScrollView } from 'react-native' import { Animated, LayoutAnimation, ScrollView } from 'react-native'
import { useQuery } from 'react-query' import { useQuery } from 'react-query'
import { accountFetch } from '@utils/fetches/accountFetch' import { accountFetch } from '@utils/fetches/accountFetch'
@ -73,10 +73,10 @@ const ScreenSharedAccount: React.FC<Props> = ({
}, },
navigation navigation
}) => { }) => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
const localAccountId = useSelector(getLocalAccountId) const localAccountId = useSelector(getLocalAccountId)
const { data } = useQuery(['Account', { id: account.id }], accountFetch) const { data } = useQuery(['Account', { id: account.id }], accountFetch)
// const stateRelationships = useSelector(relationshipsState)
const scrollY = useRef(new Animated.Value(0)).current const scrollY = useRef(new Animated.Value(0)).current
const [accountState, accountDispatch] = useReducer( const [accountState, accountDispatch] = useReducer(
accountReducer, accountReducer,
@ -104,11 +104,14 @@ const ScreenSharedAccount: React.FC<Props> = ({
scrollY={scrollY} scrollY={scrollY}
account={data} account={data}
/> />
{accountState.informationLayout?.height &&
accountState.informationLayout.y ? (
<AccountSegmentedControl <AccountSegmentedControl
accountState={accountState} accountState={accountState}
accountDispatch={accountDispatch} accountDispatch={accountDispatch}
scrollY={scrollY} scrollY={scrollY}
/> />
) : null}
<ScrollView <ScrollView
bounces={false} bounces={false}
onScroll={Animated.event( onScroll={Animated.event(

View File

@ -1,5 +1,5 @@
import React, { Dispatch, useEffect, useRef, useState } from 'react' import React, { Dispatch, useEffect, useState } from 'react'
import { Animated, Dimensions, Image, StyleSheet } from 'react-native' import { Dimensions, Image, StyleSheet, View } from 'react-native'
import { AccountAction, AccountState } from '../Account' import { AccountAction, AccountState } from '../Account'
export interface Props { export interface Props {
@ -15,19 +15,14 @@ const AccountHeader: React.FC<Props> = ({
account, account,
limitHeight = false limitHeight = false
}) => { }) => {
const [imageShown, setImageShown] = useState(true) const [ratio, setRatio] = useState(accountState.headerRatio)
useEffect(() => { useEffect(() => {
if (account?.header) { if (
if (account.header.includes('/headers/original/missing.png')) { account?.header &&
animateNewSize(accountState.headerRatio) !account.header.includes('/headers/original/missing.png')
} else { ) {
if (account.header !== account.header_static) { Image.getSize(account.header, (width, height) => {
setImageShown(false)
}
Image.getSize(
account.header,
(width, height) => {
if (!limitHeight) { if (!limitHeight) {
accountDispatch && accountDispatch &&
accountDispatch({ accountDispatch({
@ -35,42 +30,17 @@ const AccountHeader: React.FC<Props> = ({
payload: height / width payload: height / width
}) })
} }
animateNewSize( setRatio(limitHeight ? accountState.headerRatio : height / width)
limitHeight ? accountState.headerRatio : height / width })
)
},
() => {
animateNewSize(accountState.headerRatio)
}
)
}
} }
}, [account]) }, [account])
const theImage = imageShown ? (
<Image source={{ uri: account?.header }} style={styles.image} />
) : null
const windowWidth = Dimensions.get('window').width const windowWidth = Dimensions.get('window').width
const imageHeight = useRef(
new Animated.Value(windowWidth * accountState.headerRatio)
).current
const animateNewSize = (ratio: number) => {
Animated.timing(imageHeight, {
toValue: windowWidth * ratio,
duration: 350,
useNativeDriver: false
}).start(({ finished }) => {
if (finished) {
setImageShown(true)
}
})
}
return ( return (
<Animated.View style={[styles.imageContainer, { height: imageHeight }]}> <View style={[styles.imageContainer, { height: windowWidth * ratio }]}>
{theImage} <Image source={{ uri: account?.header }} style={styles.image} />
</Animated.View> </View>
) )
} }

View File

@ -49,9 +49,14 @@ const fireMutation = async ({
export interface Props { export interface Props {
accountDispatch?: Dispatch<AccountAction> accountDispatch?: Dispatch<AccountAction>
account: Mastodon.Account | undefined account: Mastodon.Account | undefined
disableActions?: boolean
} }
const AccountInformation: React.FC<Props> = ({ accountDispatch, account }) => { const AccountInformation: React.FC<Props> = ({
accountDispatch,
account,
disableActions = false
}) => {
const navigation = useNavigation() const navigation = useNavigation()
const { t } = useTranslation('sharedAccount') const { t } = useTranslation('sharedAccount')
const { theme } = useTheme() const { theme } = useTheme()
@ -165,6 +170,7 @@ const AccountInformation: React.FC<Props> = ({ accountDispatch, account }) => {
onLoadEnd={() => setAvatarLoaded(true)} onLoadEnd={() => setAvatarLoaded(true)}
/> />
</ShimmerPlaceholder> </ShimmerPlaceholder>
{!disableActions && (
<View style={styles.actions}> <View style={styles.actions}>
<ButtonRow <ButtonRow
icon='mail' icon='mail'
@ -181,6 +187,7 @@ const AccountInformation: React.FC<Props> = ({ accountDispatch, account }) => {
/> />
{followingButton} {followingButton}
</View> </View>
)}
</View> </View>
<ShimmerPlaceholder <ShimmerPlaceholder

View File

@ -35,7 +35,8 @@ const AccountSegmentedControl: React.FC<Props> = ({
(accountState.informationLayout?.height || 0) + (accountState.informationLayout?.height || 0) +
headerHeight headerHeight
], ],
extrapolate: 'clamp' extrapolate: 'clamp',
easing: undefined
}) })
return ( return (

View File

@ -13,6 +13,7 @@ import {
Alert, Alert,
Keyboard, Keyboard,
KeyboardAvoidingView, KeyboardAvoidingView,
LayoutAnimation,
StyleSheet, StyleSheet,
Text, Text,
TextInput TextInput
@ -330,6 +331,7 @@ export interface Props {
} }
const Compose: React.FC<Props> = ({ route: { params }, navigation }) => { const Compose: React.FC<Props> = ({ route: { params }, navigation }) => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
const { theme } = useTheme() const { theme } = useTheme()
const queryClient = useQueryClient() const queryClient = useQueryClient()