mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import React, {useRef} from 'react' 2import {observer} from 'mobx-react-lite' 3import {ActivityIndicator, FlatList, View} from 'react-native' 4import { 5 PostThreadViewModel, 6 PostThreadViewPostModel, 7} from '../../../state/models/post-thread-view' 8import {PostThreadItem} from './PostThreadItem' 9import {ErrorMessage} from '../util/error/ErrorMessage' 10 11export const PostThread = observer(function PostThread({ 12 uri, 13 view, 14}: { 15 uri: string 16 view: PostThreadViewModel 17}) { 18 const ref = useRef<FlatList>(null) 19 const posts = view.thread ? Array.from(flattenThread(view.thread)) : [] 20 const onRefresh = () => { 21 view 22 ?.refresh() 23 .catch(err => 24 view.rootStore.log.error('Failed to refresh posts thread', err), 25 ) 26 } 27 const onLayout = () => { 28 const index = posts.findIndex(post => post._isHighlightedPost) 29 if (index !== -1) { 30 ref.current?.scrollToIndex({ 31 index, 32 animated: false, 33 viewOffset: 40, 34 }) 35 } 36 } 37 const onScrollToIndexFailed = (info: { 38 index: number 39 highestMeasuredFrameIndex: number 40 averageItemLength: number 41 }) => { 42 ref.current?.scrollToOffset({ 43 animated: false, 44 offset: info.averageItemLength * info.index, 45 }) 46 } 47 48 // loading 49 // = 50 if ((view.isLoading && !view.isRefreshing) || view.params.uri !== uri) { 51 return ( 52 <View> 53 <ActivityIndicator /> 54 </View> 55 ) 56 } 57 58 // error 59 // = 60 if (view.hasError) { 61 return ( 62 <View> 63 <ErrorMessage 64 message={view.error} 65 style={{margin: 6}} 66 onPressTryAgain={onRefresh} 67 /> 68 </View> 69 ) 70 } 71 72 // loaded 73 // = 74 const renderItem = ({item}: {item: PostThreadViewPostModel}) => ( 75 <PostThreadItem item={item} onPostReply={onRefresh} /> 76 ) 77 return ( 78 <FlatList 79 ref={ref} 80 data={posts} 81 keyExtractor={item => item._reactKey} 82 renderItem={renderItem} 83 refreshing={view.isRefreshing} 84 onRefresh={onRefresh} 85 onLayout={onLayout} 86 onScrollToIndexFailed={onScrollToIndexFailed} 87 style={{flex: 1}} 88 contentContainerStyle={{paddingBottom: 200}} 89 /> 90 ) 91}) 92 93function* flattenThread( 94 post: PostThreadViewPostModel, 95 isAscending = false, 96): Generator<PostThreadViewPostModel, void> { 97 if (post.parent) { 98 if ('notFound' in post.parent && post.parent.notFound) { 99 // TODO render not found 100 } else { 101 yield* flattenThread(post.parent as PostThreadViewPostModel, true) 102 } 103 } 104 yield post 105 if (post.replies?.length) { 106 for (const reply of post.replies) { 107 if ('notFound' in reply && reply.notFound) { 108 // TODO render not found 109 } else { 110 yield* flattenThread(reply as PostThreadViewPostModel) 111 } 112 } 113 } else if (!isAscending && !post.parent && post.post.replyCount > 0) { 114 post._hasMore = true 115 } 116}