import { HeaderLeft } from '@components/Header' import Timeline from '@components/Timeline' import TimelineDefault from '@components/Timeline/Default' import { TabSharedStackScreenProps } from '@utils/navigation/navigators' import { QueryKeyTimeline } from '@utils/queryHooks/timeline' import React, { useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { FlatList, View } from 'react-native' import { InfiniteQueryObserver, useQueryClient } from '@tanstack/react-query' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' const TabSharedToot: React.FC> = ({ navigation, route: { params: { toot, rootQueryKey } } }) => { const { colors } = useTheme() const { t } = useTranslation('screenTabs') useEffect(() => { navigation.setOptions({ title: t('shared.toot.name'), headerLeft: () => navigation.goBack()} /> }) }, []) const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Toot', toot: toot.id }] const flRef = useRef(null) const [itemsLength, setItemsLength] = useState(0) const scrolled = useRef(false) const queryClient = useQueryClient() const observer = new InfiniteQueryObserver(queryClient, { queryKey, enabled: false }) const replyLevels = useRef<{ id: string; level: number }[]>([]) const data = useRef() const highlightIndex = useRef(0) useEffect(() => { return observer.subscribe(result => { if (result.isSuccess) { const flattenData = result.data?.pages ? // @ts-ignore result.data.pages.flatMap(d => [...d.body]) : [] // Auto go back when toot page is empty if (flattenData.length < 1) { navigation.goBack() return } data.current = flattenData highlightIndex.current = flattenData.findIndex(({ id }) => id === toot.id) for (const [index, status] of flattenData.entries()) { if (status.id === toot.id) continue if (status.in_reply_to_id === toot.id) continue if (!replyLevels.current.find(reply => reply.id === status.in_reply_to_id)) { const prevLevel = replyLevels.current.find(reply => reply.id === flattenData[index - 1].in_reply_to_id) ?.level || 0 replyLevels.current.push({ id: status.in_reply_to_id, level: prevLevel + 1 }) } } setItemsLength(flattenData.length) if (!scrolled.current) { scrolled.current = true const pointer = flattenData.findIndex(({ id }) => id === toot.id) if (pointer < 1) return const length = flRef.current?.props.data?.length if (!length) return try { setTimeout(() => { try { flRef.current?.scrollToIndex({ index: pointer, viewOffset: 100 }) } catch {} }, 500) } catch (error) { return } } } }) }, [scrolled.current, replyLevels.current]) return ( { const levels = { previous: replyLevels.current.find( reply => reply.id === data.current?.[index - 1]?.in_reply_to_id )?.level || 0, current: replyLevels.current.find(reply => reply.id === item.in_reply_to_id)?.level || 0, next: replyLevels.current.find( reply => reply.id === data.current?.[index + 1]?.in_reply_to_id )?.level || 0 } return ( <> {Array.from(Array(levels.current)).map((_, i) => { if (index < highlightIndex.current) return null if ( levels.previous + 1 === levels.current || (levels.previous && levels.current && levels.previous === levels.current) ) { return ( ) } else { return null } })} {Array.from(Array(levels.next)).map((_, i) => { if (index < highlightIndex.current) return null if ( levels.current + 1 === levels.next || (levels.current && levels.next && levels.current === levels.next) ) { return ( i + 1 ? StyleConstants.Spacing.Global.PagePadding + StyleConstants.Avatar.XS : StyleConstants.Spacing.Global.PagePadding, left: StyleConstants.Spacing.Global.PagePadding / 2 + 8 * i, height: 200, borderLeftColor: colors.border, borderLeftWidth: 1 }} /> ) } else { return null } })} ) }, onScrollToIndexFailed: error => { const offset = error.averageItemLength * error.index flRef.current?.scrollToOffset({ offset }) try { error.index < itemsLength && setTimeout( () => flRef.current?.scrollToIndex({ index: error.index, viewOffset: 100 }), 500 ) } catch {} } }} disableRefresh disableInfinity /> ) } export default TabSharedToot