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