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