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}