forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
1import {useCallback, useEffect, useImperativeHandle, useState} from 'react'
2import {View} from 'react-native'
3import {msg} from '@lingui/core/macro'
4import {useLingui} from '@lingui/react'
5import {Trans} from '@lingui/react/macro'
6import {useIsFocused} from '@react-navigation/native'
7import {useQueryClient} from '@tanstack/react-query'
8
9import {listenSoftReset} from '#/state/events'
10import {
11 type FeedDescriptor,
12 RQKEY as FEED_RQKEY,
13} from '#/state/queries/post-feed'
14import {PostFeed} from '#/view/com/posts/PostFeed'
15import {EmptyState} from '#/view/com/util/EmptyState'
16import {type ListRef} from '#/view/com/util/List'
17import {LoadLatestBtn} from '#/view/com/util/load-latest/LoadLatestBtn'
18import {atoms as a} from '#/alf'
19import {Button, ButtonIcon, ButtonText} from '#/components/Button'
20import {HashtagWide_Stroke1_Corner0_Rounded as HashtagWideIcon} from '#/components/icons/Hashtag'
21import {PersonPlus_Stroke2_Corner0_Rounded as PersonPlusIcon} from '#/components/icons/Person'
22import {IS_NATIVE} from '#/env'
23
24interface SectionRef {
25 scrollToTop: () => void
26}
27
28interface FeedSectionProps {
29 ref?: React.Ref<SectionRef>
30 feed: FeedDescriptor
31 headerHeight: number
32 scrollElRef: ListRef
33 isFocused: boolean
34 isOwner: boolean
35 onPressAddUser: () => void
36}
37
38export function FeedSection({
39 ref,
40 feed,
41 scrollElRef,
42 headerHeight,
43 isFocused,
44 isOwner,
45 onPressAddUser,
46}: FeedSectionProps) {
47 const queryClient = useQueryClient()
48 const [hasNew, setHasNew] = useState(false)
49 const [isScrolledDown, setIsScrolledDown] = useState(false)
50 const isScreenFocused = useIsFocused()
51 const {_} = useLingui()
52
53 const onScrollToTop = useCallback(() => {
54 scrollElRef.current?.scrollToOffset({
55 animated: IS_NATIVE,
56 offset: -headerHeight,
57 })
58 queryClient.resetQueries({queryKey: FEED_RQKEY(feed)})
59 setHasNew(false)
60 }, [scrollElRef, headerHeight, queryClient, feed, setHasNew])
61 useImperativeHandle(ref, () => ({
62 scrollToTop: onScrollToTop,
63 }))
64
65 useEffect(() => {
66 if (!isScreenFocused) {
67 return
68 }
69 return listenSoftReset(onScrollToTop)
70 }, [onScrollToTop, isScreenFocused])
71
72 const renderPostsEmpty = useCallback(() => {
73 return (
74 <View style={[a.gap_xl, a.align_center]}>
75 <EmptyState
76 icon={HashtagWideIcon}
77 iconSize="2xl"
78 message={_(msg`This feed is empty.`)}
79 />
80 {isOwner && (
81 <Button
82 label={_(msg`Start adding people`)}
83 onPress={onPressAddUser}
84 color="primary"
85 size="small">
86 <ButtonIcon icon={PersonPlusIcon} />
87 <ButtonText>
88 <Trans>Start adding people!</Trans>
89 </ButtonText>
90 </Button>
91 )}
92 </View>
93 )
94 }, [_, onPressAddUser, isOwner])
95
96 return (
97 <View>
98 <PostFeed
99 testID="listFeed"
100 enabled={isFocused}
101 feed={feed}
102 pollInterval={60e3}
103 disablePoll={hasNew}
104 scrollElRef={scrollElRef}
105 onHasNew={setHasNew}
106 onScrolledDownChange={setIsScrolledDown}
107 renderEmptyState={renderPostsEmpty}
108 headerOffset={headerHeight}
109 />
110 {(isScrolledDown || hasNew) && (
111 <LoadLatestBtn
112 onPress={onScrollToTop}
113 label={_(msg`Load new posts`)}
114 showIndicator={hasNew}
115 />
116 )}
117 </View>
118 )
119}