2020-12-12 22:19:18 +01:00
|
|
|
import React, { useCallback, useEffect, useMemo, useRef } from 'react'
|
2020-12-20 23:49:35 +01:00
|
|
|
import { RefreshControl, StyleSheet } from 'react-native'
|
2020-12-19 18:21:37 +01:00
|
|
|
import { InfiniteData, useInfiniteQuery } from 'react-query'
|
2020-10-23 09:22:17 +02:00
|
|
|
|
2020-12-13 14:04:25 +01:00
|
|
|
import TimelineNotifications from '@components/Timelines/Timeline/Notifications'
|
|
|
|
import TimelineDefault from '@components/Timelines/Timeline/Default'
|
|
|
|
import TimelineConversation from '@components/Timelines/Timeline/Conversation'
|
|
|
|
import { timelineFetch } from '@utils/fetches/timelineFetch'
|
|
|
|
import TimelineSeparator from '@components/Timelines/Timeline/Separator'
|
|
|
|
import TimelineEmpty from '@components/Timelines/Timeline/Empty'
|
|
|
|
import TimelineEnd from '@components/Timelines/Timeline/Shared/End'
|
2020-12-13 23:02:54 +01:00
|
|
|
import { useScrollToTop } from '@react-navigation/native'
|
2020-12-17 12:04:58 +01:00
|
|
|
import { FlatList } from 'react-native-gesture-handler'
|
2020-12-29 01:09:22 +01:00
|
|
|
import { useDispatch } from 'react-redux'
|
|
|
|
import { updateNotification } from '@root/utils/slices/instancesSlice'
|
2020-10-24 18:07:09 +02:00
|
|
|
|
2020-12-19 18:21:37 +01:00
|
|
|
export type TimelineData =
|
|
|
|
| InfiniteData<{
|
|
|
|
toots: Mastodon.Status[]
|
|
|
|
pointer?: number | undefined
|
|
|
|
pinnedLength?: number | undefined
|
|
|
|
}>
|
|
|
|
| undefined
|
|
|
|
|
2020-11-04 22:26:38 +01:00
|
|
|
export interface Props {
|
2020-11-21 00:40:55 +01:00
|
|
|
page: App.Pages
|
2020-10-31 21:04:46 +01:00
|
|
|
hashtag?: string
|
|
|
|
list?: string
|
2020-12-12 12:49:29 +01:00
|
|
|
toot?: Mastodon.Status
|
2020-10-31 21:04:46 +01:00
|
|
|
account?: string
|
|
|
|
disableRefresh?: boolean
|
2020-11-04 22:26:38 +01:00
|
|
|
}
|
2020-10-23 09:22:17 +02:00
|
|
|
|
2020-11-04 22:26:38 +01:00
|
|
|
const Timeline: React.FC<Props> = ({
|
|
|
|
page,
|
|
|
|
hashtag,
|
|
|
|
list,
|
|
|
|
toot,
|
|
|
|
account,
|
2020-12-18 00:00:45 +01:00
|
|
|
disableRefresh = false
|
2020-11-04 22:26:38 +01:00
|
|
|
}) => {
|
2020-12-18 23:58:53 +01:00
|
|
|
const queryKey: QueryKey.Timeline = [
|
2020-12-13 00:22:49 +01:00
|
|
|
page,
|
|
|
|
{
|
|
|
|
...(hashtag && { hashtag }),
|
|
|
|
...(list && { list }),
|
|
|
|
...(toot && { toot }),
|
|
|
|
...(account && { account })
|
|
|
|
}
|
|
|
|
]
|
2020-11-04 22:26:38 +01:00
|
|
|
const {
|
2020-12-13 01:24:25 +01:00
|
|
|
status,
|
2020-11-04 22:26:38 +01:00
|
|
|
data,
|
2020-12-18 23:58:53 +01:00
|
|
|
refetch,
|
2020-12-21 22:08:29 +01:00
|
|
|
isSuccess,
|
2020-12-18 23:58:53 +01:00
|
|
|
hasPreviousPage,
|
|
|
|
fetchPreviousPage,
|
|
|
|
isFetchingPreviousPage,
|
|
|
|
hasNextPage,
|
2020-12-26 12:59:16 +01:00
|
|
|
fetchNextPage,
|
|
|
|
isFetchingNextPage
|
2020-12-12 18:22:22 +01:00
|
|
|
} = useInfiniteQuery(queryKey, timelineFetch, {
|
2020-12-21 13:56:04 +01:00
|
|
|
getPreviousPageParam: firstPage => {
|
|
|
|
return firstPage.toots.length
|
|
|
|
? {
|
|
|
|
direction: 'prev',
|
|
|
|
id: firstPage.toots[0].id
|
|
|
|
}
|
|
|
|
: undefined
|
2020-12-20 23:49:35 +01:00
|
|
|
},
|
2020-12-21 22:58:53 +01:00
|
|
|
getNextPageParam: lastPage => {
|
2020-12-21 13:56:04 +01:00
|
|
|
return lastPage.toots.length
|
2020-12-18 23:58:53 +01:00
|
|
|
? {
|
|
|
|
direction: 'next',
|
|
|
|
id: lastPage.toots[lastPage.toots.length - 1].id
|
|
|
|
}
|
|
|
|
: undefined
|
2020-12-21 13:56:04 +01:00
|
|
|
}
|
2020-12-12 18:22:22 +01:00
|
|
|
})
|
2020-12-18 23:58:53 +01:00
|
|
|
const flattenData = data?.pages ? data.pages.flatMap(d => [...d?.toots]) : []
|
|
|
|
const flattenPointer = data?.pages
|
|
|
|
? data.pages.flatMap(d => [d?.pointer])
|
|
|
|
: []
|
|
|
|
const flattenPinnedLength = data?.pages
|
|
|
|
? data.pages.flatMap(d => [d?.pinnedLength])
|
|
|
|
: []
|
2020-12-12 12:49:29 +01:00
|
|
|
|
2020-12-29 01:09:22 +01:00
|
|
|
// Clear unread notification badge
|
|
|
|
const dispatch = useDispatch()
|
|
|
|
useEffect(() => {
|
|
|
|
if (page === 'Notifications' && flattenData.length) {
|
|
|
|
dispatch(
|
|
|
|
updateNotification({
|
|
|
|
unread: false,
|
|
|
|
latestTime: flattenData[0].created_at
|
|
|
|
})
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}, [flattenData])
|
|
|
|
|
2020-12-17 12:04:58 +01:00
|
|
|
const flRef = useRef<FlatList<any>>(null)
|
2020-12-12 12:49:29 +01:00
|
|
|
useEffect(() => {
|
2020-12-21 22:08:29 +01:00
|
|
|
if (toot && isSuccess) {
|
2020-12-12 12:49:29 +01:00
|
|
|
setTimeout(() => {
|
|
|
|
flRef.current?.scrollToIndex({
|
2020-12-14 23:44:57 +01:00
|
|
|
index: flattenPointer[0]!,
|
2020-12-12 12:49:29 +01:00
|
|
|
viewOffset: 100
|
|
|
|
})
|
|
|
|
}, 500)
|
|
|
|
}
|
2020-12-21 22:08:29 +01:00
|
|
|
}, [isSuccess])
|
2020-10-23 09:22:17 +02:00
|
|
|
|
2020-12-21 22:58:53 +01:00
|
|
|
const keyExtractor = useCallback(({ id }) => id, [])
|
|
|
|
const renderItem = useCallback(
|
2020-12-20 17:53:24 +01:00
|
|
|
({ item, index }) => {
|
|
|
|
switch (page) {
|
|
|
|
case 'Conversations':
|
2020-12-27 16:25:29 +01:00
|
|
|
return (
|
|
|
|
<TimelineConversation conversation={item} queryKey={queryKey} />
|
|
|
|
)
|
2020-12-20 17:53:24 +01:00
|
|
|
case 'Notifications':
|
|
|
|
return (
|
|
|
|
<TimelineNotifications notification={item} queryKey={queryKey} />
|
|
|
|
)
|
|
|
|
default:
|
|
|
|
return (
|
|
|
|
<TimelineDefault
|
|
|
|
item={item}
|
|
|
|
queryKey={queryKey}
|
|
|
|
index={index}
|
|
|
|
{...(flattenPinnedLength &&
|
|
|
|
flattenPinnedLength[0] && {
|
|
|
|
pinnedLength: flattenPinnedLength[0]
|
|
|
|
})}
|
|
|
|
{...(toot && toot.id === item.id && { highlighted: true })}
|
|
|
|
/>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
[flattenPinnedLength[0]]
|
|
|
|
)
|
2020-12-21 22:58:53 +01:00
|
|
|
const ItemSeparatorComponent = useCallback(
|
2020-12-12 22:19:18 +01:00
|
|
|
({ leadingItem }) => (
|
|
|
|
<TimelineSeparator
|
|
|
|
{...(toot && toot.id === leadingItem.id && { highlighted: true })}
|
|
|
|
/>
|
|
|
|
),
|
|
|
|
[]
|
|
|
|
)
|
|
|
|
const flItemEmptyComponent = useMemo(
|
2020-12-13 01:24:25 +01:00
|
|
|
() => <TimelineEmpty status={status} refetch={refetch} />,
|
2020-12-18 23:58:53 +01:00
|
|
|
[status]
|
2020-12-12 22:19:18 +01:00
|
|
|
)
|
2020-12-26 12:59:16 +01:00
|
|
|
const onEndReached = useCallback(
|
|
|
|
() => !disableRefresh && !isFetchingNextPage && fetchNextPage(),
|
|
|
|
[isFetchingNextPage]
|
|
|
|
)
|
2020-12-21 22:58:53 +01:00
|
|
|
const ListFooterComponent = useCallback(
|
2020-12-25 21:51:46 +01:00
|
|
|
() => <TimelineEnd hasNextPage={!disableRefresh ? hasNextPage : false} />,
|
2020-12-19 01:57:57 +01:00
|
|
|
[hasNextPage]
|
|
|
|
)
|
2020-12-21 22:58:53 +01:00
|
|
|
const refreshControl = useMemo(
|
2020-12-20 23:49:35 +01:00
|
|
|
() => (
|
|
|
|
<RefreshControl
|
2020-12-21 22:58:53 +01:00
|
|
|
refreshing={isFetchingPreviousPage}
|
|
|
|
onRefresh={() => fetchPreviousPage()}
|
2020-12-20 23:49:35 +01:00
|
|
|
/>
|
|
|
|
),
|
2020-12-21 22:58:53 +01:00
|
|
|
[isFetchingPreviousPage]
|
2020-12-20 23:49:35 +01:00
|
|
|
)
|
2020-12-12 12:49:29 +01:00
|
|
|
const onScrollToIndexFailed = useCallback(error => {
|
|
|
|
const offset = error.averageItemLength * error.index
|
|
|
|
flRef.current?.scrollToOffset({ offset })
|
|
|
|
setTimeout(
|
|
|
|
() =>
|
|
|
|
flRef.current?.scrollToIndex({ index: error.index, viewOffset: 100 }),
|
|
|
|
350
|
2020-10-26 00:27:53 +01:00
|
|
|
)
|
2020-12-12 12:49:29 +01:00
|
|
|
}, [])
|
2020-10-23 09:22:17 +02:00
|
|
|
|
2020-12-13 23:02:54 +01:00
|
|
|
useScrollToTop(flRef)
|
|
|
|
|
2020-12-12 12:49:29 +01:00
|
|
|
return (
|
|
|
|
<FlatList
|
|
|
|
ref={flRef}
|
2020-12-20 23:49:35 +01:00
|
|
|
windowSize={11}
|
2020-12-12 12:49:29 +01:00
|
|
|
data={flattenData}
|
2020-12-20 23:49:35 +01:00
|
|
|
initialNumToRender={5}
|
|
|
|
maxToRenderPerBatch={5}
|
2020-12-12 12:49:29 +01:00
|
|
|
style={styles.flatList}
|
2020-12-21 22:58:53 +01:00
|
|
|
renderItem={renderItem}
|
|
|
|
onEndReached={onEndReached}
|
|
|
|
keyExtractor={keyExtractor}
|
|
|
|
onEndReachedThreshold={0.75}
|
|
|
|
ListFooterComponent={ListFooterComponent}
|
2020-12-12 22:19:18 +01:00
|
|
|
ListEmptyComponent={flItemEmptyComponent}
|
2020-12-21 22:58:53 +01:00
|
|
|
{...(!disableRefresh && { refreshControl })}
|
|
|
|
ItemSeparatorComponent={ItemSeparatorComponent}
|
2020-12-21 22:08:29 +01:00
|
|
|
{...(toot && isSuccess && { onScrollToIndexFailed })}
|
2020-12-12 12:49:29 +01:00
|
|
|
/>
|
|
|
|
)
|
2020-10-23 09:22:17 +02:00
|
|
|
}
|
|
|
|
|
2020-11-28 17:07:30 +01:00
|
|
|
const styles = StyleSheet.create({
|
|
|
|
flatList: {
|
|
|
|
minHeight: '100%'
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2020-10-31 21:04:46 +01:00
|
|
|
export default Timeline
|