mirror of
https://github.com/tooot-app/app
synced 2025-01-06 23:01:51 +01:00
Retry refresh
This commit is contained in:
parent
1ee7dd920d
commit
c47d4cb4c4
@ -50,14 +50,12 @@ const Timeline: React.FC<Props> = ({
|
||||
status,
|
||||
data,
|
||||
refetch,
|
||||
isFetching,
|
||||
isSuccess,
|
||||
hasPreviousPage,
|
||||
fetchPreviousPage,
|
||||
isFetchingPreviousPage,
|
||||
hasNextPage,
|
||||
fetchNextPage,
|
||||
isFetchingNextPage
|
||||
fetchNextPage
|
||||
} = useInfiniteQuery(queryKey, timelineFetch, {
|
||||
getPreviousPageParam: firstPage => {
|
||||
return firstPage.toots.length
|
||||
@ -67,7 +65,7 @@ const Timeline: React.FC<Props> = ({
|
||||
}
|
||||
: undefined
|
||||
},
|
||||
getNextPageParam: (lastPage, all) => {
|
||||
getNextPageParam: lastPage => {
|
||||
return lastPage.toots.length
|
||||
? {
|
||||
direction: 'next',
|
||||
@ -96,8 +94,8 @@ const Timeline: React.FC<Props> = ({
|
||||
}
|
||||
}, [isSuccess])
|
||||
|
||||
const flKeyExtrator = useCallback(({ id }) => id, [])
|
||||
const flRenderItem = useCallback(
|
||||
const keyExtractor = useCallback(({ id }) => id, [])
|
||||
const renderItem = useCallback(
|
||||
({ item, index }) => {
|
||||
switch (page) {
|
||||
case 'Conversations':
|
||||
@ -123,7 +121,7 @@ const Timeline: React.FC<Props> = ({
|
||||
},
|
||||
[flattenPinnedLength[0]]
|
||||
)
|
||||
const flItemSeparatorComponent = useCallback(
|
||||
const ItemSeparatorComponent = useCallback(
|
||||
({ leadingItem }) => (
|
||||
<TimelineSeparator
|
||||
{...(toot && toot.id === leadingItem.id && { highlighted: true })}
|
||||
@ -135,27 +133,19 @@ const Timeline: React.FC<Props> = ({
|
||||
() => <TimelineEmpty status={status} refetch={refetch} />,
|
||||
[status]
|
||||
)
|
||||
const flOnRefresh = useCallback(() => {
|
||||
!disableRefresh &&
|
||||
(hasPreviousPage
|
||||
? fetchPreviousPage()
|
||||
: !isFetching
|
||||
? refetch()
|
||||
: undefined)
|
||||
}, [hasPreviousPage, isFetching])
|
||||
const flOnEndReach = useCallback(() => !disableRefresh && fetchNextPage(), [])
|
||||
const flFooter = useCallback(
|
||||
() => (!disableRefresh ? <TimelineEnd hasNextPage={hasNextPage} /> : null),
|
||||
const onEndReached = useCallback(() => fetchNextPage(), [])
|
||||
const ListFooterComponent = useCallback(
|
||||
() => <TimelineEnd hasNextPage={hasNextPage} />,
|
||||
[hasNextPage]
|
||||
)
|
||||
const flRefreshControl = useMemo(
|
||||
const refreshControl = useMemo(
|
||||
() => (
|
||||
<RefreshControl
|
||||
refreshing={isFetchingPreviousPage || isFetching}
|
||||
onRefresh={flOnRefresh}
|
||||
refreshing={isFetchingPreviousPage}
|
||||
onRefresh={() => fetchPreviousPage()}
|
||||
/>
|
||||
),
|
||||
[isFetchingPreviousPage, isFetching]
|
||||
[isFetchingPreviousPage]
|
||||
)
|
||||
const onScrollToIndexFailed = useCallback(error => {
|
||||
const offset = error.averageItemLength * error.index
|
||||
@ -177,14 +167,14 @@ const Timeline: React.FC<Props> = ({
|
||||
initialNumToRender={5}
|
||||
maxToRenderPerBatch={5}
|
||||
style={styles.flatList}
|
||||
renderItem={flRenderItem}
|
||||
onEndReached={flOnEndReach}
|
||||
keyExtractor={flKeyExtrator}
|
||||
ListFooterComponent={flFooter}
|
||||
refreshControl={flRefreshControl}
|
||||
renderItem={renderItem}
|
||||
onEndReached={onEndReached}
|
||||
keyExtractor={keyExtractor}
|
||||
onEndReachedThreshold={0.75}
|
||||
ListFooterComponent={ListFooterComponent}
|
||||
ListEmptyComponent={flItemEmptyComponent}
|
||||
ItemSeparatorComponent={flItemSeparatorComponent}
|
||||
onEndReachedThreshold={!disableRefresh ? 0.75 : null}
|
||||
{...(!disableRefresh && { refreshControl })}
|
||||
ItemSeparatorComponent={ItemSeparatorComponent}
|
||||
{...(toot && isSuccess && { onScrollToIndexFailed })}
|
||||
/>
|
||||
)
|
||||
|
@ -17,7 +17,7 @@ const TimelineAvatar: React.FC<Props> = ({ queryKey, account }) => {
|
||||
|
||||
return (
|
||||
<Pressable style={styles.avatar} onPress={onPress}>
|
||||
<Image source={{ uri: account.avatar }} style={styles.image} />
|
||||
<Image source={{ uri: account.avatar_static }} style={styles.image} />
|
||||
</Pressable>
|
||||
)
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
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 { getLocalUrl } from '@utils/slices/instancesSlice'
|
||||
@ -14,6 +14,7 @@ import { AccountState } from '../Shared/Account'
|
||||
import AccountNav from '../Shared/Account/Nav'
|
||||
|
||||
const ScreenMeRoot: React.FC = () => {
|
||||
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
|
||||
const localRegistered = useSelector(getLocalUrl)
|
||||
|
||||
const scrollRef = useRef<ScrollView>(null)
|
||||
|
@ -28,7 +28,7 @@ const MyInfo: React.FC<Props> = ({ setData }) => {
|
||||
account={data}
|
||||
limitHeight
|
||||
/>
|
||||
<AccountInformation account={data} />
|
||||
<AccountInformation account={data} disableActions />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
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 { accountFetch } from '@utils/fetches/accountFetch'
|
||||
@ -73,10 +73,10 @@ const ScreenSharedAccount: React.FC<Props> = ({
|
||||
},
|
||||
navigation
|
||||
}) => {
|
||||
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
|
||||
const localAccountId = useSelector(getLocalAccountId)
|
||||
const { data } = useQuery(['Account', { id: account.id }], accountFetch)
|
||||
|
||||
// const stateRelationships = useSelector(relationshipsState)
|
||||
const scrollY = useRef(new Animated.Value(0)).current
|
||||
const [accountState, accountDispatch] = useReducer(
|
||||
accountReducer,
|
||||
@ -104,11 +104,14 @@ const ScreenSharedAccount: React.FC<Props> = ({
|
||||
scrollY={scrollY}
|
||||
account={data}
|
||||
/>
|
||||
<AccountSegmentedControl
|
||||
accountState={accountState}
|
||||
accountDispatch={accountDispatch}
|
||||
scrollY={scrollY}
|
||||
/>
|
||||
{accountState.informationLayout?.height &&
|
||||
accountState.informationLayout.y ? (
|
||||
<AccountSegmentedControl
|
||||
accountState={accountState}
|
||||
accountDispatch={accountDispatch}
|
||||
scrollY={scrollY}
|
||||
/>
|
||||
) : null}
|
||||
<ScrollView
|
||||
bounces={false}
|
||||
onScroll={Animated.event(
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { Dispatch, useEffect, useRef, useState } from 'react'
|
||||
import { Animated, Dimensions, Image, StyleSheet } from 'react-native'
|
||||
import React, { Dispatch, useEffect, useState } from 'react'
|
||||
import { Dimensions, Image, StyleSheet, View } from 'react-native'
|
||||
import { AccountAction, AccountState } from '../Account'
|
||||
|
||||
export interface Props {
|
||||
@ -15,62 +15,32 @@ const AccountHeader: React.FC<Props> = ({
|
||||
account,
|
||||
limitHeight = false
|
||||
}) => {
|
||||
const [imageShown, setImageShown] = useState(true)
|
||||
const [ratio, setRatio] = useState(accountState.headerRatio)
|
||||
|
||||
useEffect(() => {
|
||||
if (account?.header) {
|
||||
if (account.header.includes('/headers/original/missing.png')) {
|
||||
animateNewSize(accountState.headerRatio)
|
||||
} else {
|
||||
if (account.header !== account.header_static) {
|
||||
setImageShown(false)
|
||||
if (
|
||||
account?.header &&
|
||||
!account.header.includes('/headers/original/missing.png')
|
||||
) {
|
||||
Image.getSize(account.header, (width, height) => {
|
||||
if (!limitHeight) {
|
||||
accountDispatch &&
|
||||
accountDispatch({
|
||||
type: 'headerRatio',
|
||||
payload: height / width
|
||||
})
|
||||
}
|
||||
Image.getSize(
|
||||
account.header,
|
||||
(width, height) => {
|
||||
if (!limitHeight) {
|
||||
accountDispatch &&
|
||||
accountDispatch({
|
||||
type: 'headerRatio',
|
||||
payload: height / width
|
||||
})
|
||||
}
|
||||
animateNewSize(
|
||||
limitHeight ? accountState.headerRatio : height / width
|
||||
)
|
||||
},
|
||||
() => {
|
||||
animateNewSize(accountState.headerRatio)
|
||||
}
|
||||
)
|
||||
}
|
||||
setRatio(limitHeight ? accountState.headerRatio : height / width)
|
||||
})
|
||||
}
|
||||
}, [account])
|
||||
|
||||
const theImage = imageShown ? (
|
||||
<Image source={{ uri: account?.header }} style={styles.image} />
|
||||
) : null
|
||||
|
||||
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 (
|
||||
<Animated.View style={[styles.imageContainer, { height: imageHeight }]}>
|
||||
{theImage}
|
||||
</Animated.View>
|
||||
<View style={[styles.imageContainer, { height: windowWidth * ratio }]}>
|
||||
<Image source={{ uri: account?.header }} style={styles.image} />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -49,9 +49,14 @@ const fireMutation = async ({
|
||||
export interface Props {
|
||||
accountDispatch?: Dispatch<AccountAction>
|
||||
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 { t } = useTranslation('sharedAccount')
|
||||
const { theme } = useTheme()
|
||||
@ -165,22 +170,24 @@ const AccountInformation: React.FC<Props> = ({ accountDispatch, account }) => {
|
||||
onLoadEnd={() => setAvatarLoaded(true)}
|
||||
/>
|
||||
</ShimmerPlaceholder>
|
||||
<View style={styles.actions}>
|
||||
<ButtonRow
|
||||
icon='mail'
|
||||
onPress={() =>
|
||||
navigation.navigate(getCurrentTab(navigation), {
|
||||
screen: 'Screen-Shared-Compose',
|
||||
params: {
|
||||
type: 'conversation',
|
||||
incomingStatus: { account }
|
||||
}
|
||||
})
|
||||
}
|
||||
style={{ marginRight: StyleConstants.Spacing.S }}
|
||||
/>
|
||||
{followingButton}
|
||||
</View>
|
||||
{!disableActions && (
|
||||
<View style={styles.actions}>
|
||||
<ButtonRow
|
||||
icon='mail'
|
||||
onPress={() =>
|
||||
navigation.navigate(getCurrentTab(navigation), {
|
||||
screen: 'Screen-Shared-Compose',
|
||||
params: {
|
||||
type: 'conversation',
|
||||
incomingStatus: { account }
|
||||
}
|
||||
})
|
||||
}
|
||||
style={{ marginRight: StyleConstants.Spacing.S }}
|
||||
/>
|
||||
{followingButton}
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
<ShimmerPlaceholder
|
||||
|
@ -35,7 +35,8 @@ const AccountSegmentedControl: React.FC<Props> = ({
|
||||
(accountState.informationLayout?.height || 0) +
|
||||
headerHeight
|
||||
],
|
||||
extrapolate: 'clamp'
|
||||
extrapolate: 'clamp',
|
||||
easing: undefined
|
||||
})
|
||||
|
||||
return (
|
||||
|
@ -13,6 +13,7 @@ import {
|
||||
Alert,
|
||||
Keyboard,
|
||||
KeyboardAvoidingView,
|
||||
LayoutAnimation,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TextInput
|
||||
@ -330,6 +331,7 @@ export interface Props {
|
||||
}
|
||||
|
||||
const Compose: React.FC<Props> = ({ route: { params }, navigation }) => {
|
||||
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
|
||||
const { theme } = useTheme()
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user