mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import React from 'react'
2import {ListRenderItemInfo, Pressable} from 'react-native'
3import {useFocusEffect} from '@react-navigation/native'
4import {useSetMinimalShellMode} from 'state/shell'
5import {ViewHeader} from 'view/com/util/ViewHeader'
6import {NativeStackScreenProps} from '@react-navigation/native-stack'
7import {CommonNavigatorParams} from 'lib/routes/types'
8import {useSearchPostsQuery} from 'state/queries/search-posts'
9import {Post} from 'view/com/post/Post'
10import {PostView} from '@atproto/api/dist/client/types/app/bsky/feed/defs'
11import {enforceLen} from 'lib/strings/helpers'
12import {
13 ListFooter,
14 ListHeaderDesktop,
15 ListMaybePlaceholder,
16} from '#/components/Lists'
17import {List} from 'view/com/util/List'
18import {msg} from '@lingui/macro'
19import {useLingui} from '@lingui/react'
20import {sanitizeHandle} from 'lib/strings/handles'
21import {ArrowOutOfBox_Stroke2_Corner0_Rounded} from '#/components/icons/ArrowOutOfBox'
22import {shareUrl} from 'lib/sharing'
23import {HITSLOP_10} from 'lib/constants'
24import {isNative} from 'platform/detection'
25import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender'
26
27const renderItem = ({item}: ListRenderItemInfo<PostView>) => {
28 return <Post post={item} />
29}
30
31const keyExtractor = (item: PostView, index: number) => {
32 return `${item.uri}-${index}`
33}
34
35export default function HashtagScreen({
36 route,
37}: NativeStackScreenProps<CommonNavigatorParams, 'Hashtag'>) {
38 const {tag, author} = route.params
39 const setMinimalShellMode = useSetMinimalShellMode()
40 const {_} = useLingui()
41 const initialNumToRender = useInitialNumToRender()
42 const [isPTR, setIsPTR] = React.useState(false)
43
44 const fullTag = React.useMemo(() => {
45 return `#${tag.replaceAll('%23', '#')}`
46 }, [tag])
47
48 const queryParam = React.useMemo(() => {
49 if (!author) return fullTag
50 return `${fullTag} from:${sanitizeHandle(author)}`
51 }, [fullTag, author])
52
53 const headerTitle = React.useMemo(() => {
54 return enforceLen(fullTag.toLowerCase(), 24, true, 'middle')
55 }, [fullTag])
56
57 const sanitizedAuthor = React.useMemo(() => {
58 if (!author) return
59 return sanitizeHandle(author)
60 }, [author])
61
62 const {
63 data,
64 isFetching,
65 isLoading,
66 isRefetching,
67 isError,
68 error,
69 refetch,
70 fetchNextPage,
71 hasNextPage,
72 } = useSearchPostsQuery({query: queryParam})
73
74 const posts = React.useMemo(() => {
75 return data?.pages.flatMap(page => page.posts) || []
76 }, [data])
77
78 useFocusEffect(
79 React.useCallback(() => {
80 setMinimalShellMode(false)
81 }, [setMinimalShellMode]),
82 )
83
84 const onShare = React.useCallback(() => {
85 const url = new URL('https://bsky.app')
86 url.pathname = `/hashtag/${tag}`
87 if (author) {
88 url.searchParams.set('author', author)
89 }
90 shareUrl(url.toString())
91 }, [tag, author])
92
93 const onRefresh = React.useCallback(async () => {
94 setIsPTR(true)
95 await refetch()
96 setIsPTR(false)
97 }, [refetch])
98
99 const onEndReached = React.useCallback(() => {
100 if (isFetching || !hasNextPage || error) return
101 fetchNextPage()
102 }, [isFetching, hasNextPage, error, fetchNextPage])
103
104 return (
105 <>
106 <ViewHeader
107 title={headerTitle}
108 subtitle={author ? _(msg`From @${sanitizedAuthor}`) : undefined}
109 canGoBack
110 renderButton={
111 isNative
112 ? () => (
113 <Pressable
114 accessibilityRole="button"
115 onPress={onShare}
116 hitSlop={HITSLOP_10}>
117 <ArrowOutOfBox_Stroke2_Corner0_Rounded
118 size="lg"
119 onPress={onShare}
120 />
121 </Pressable>
122 )
123 : undefined
124 }
125 />
126 <ListMaybePlaceholder
127 isLoading={isLoading || isRefetching}
128 isError={isError}
129 isEmpty={posts.length < 1}
130 onRetry={refetch}
131 notFoundType="results"
132 empty={_(msg`We couldn't find any results for that hashtag.`)}
133 />
134 {!isLoading && posts.length > 0 && (
135 <List<PostView>
136 data={posts}
137 renderItem={renderItem}
138 keyExtractor={keyExtractor}
139 refreshing={isPTR}
140 onRefresh={onRefresh}
141 onEndReached={onEndReached}
142 onEndReachedThreshold={4}
143 // @ts-ignore web only -prf
144 desktopFixedHeight
145 ListHeaderComponent={
146 <ListHeaderDesktop
147 title={headerTitle}
148 subtitle={author ? _(msg`From @${sanitizedAuthor}`) : undefined}
149 />
150 }
151 ListFooterComponent={
152 <ListFooter
153 isFetching={isFetching && !isRefetching}
154 isError={isError}
155 error={error?.name}
156 onRetry={fetchNextPage}
157 />
158 }
159 initialNumToRender={initialNumToRender}
160 windowSize={11}
161 />
162 )}
163 </>
164 )
165}