this repo has no description
1import {useMemo} from 'react'
2import {View} from 'react-native'
3import {type AtUri} from '@atproto/api'
4import {msg} from '@lingui/core/macro'
5import {useLingui} from '@lingui/react'
6
7import {PressableScale} from '#/lib/custom-animations/PressableScale'
8// import {makeProfileLink} from '#/lib/routes/links'
9// import {feedUriToHref} from '#/lib/strings/url-helpers'
10// import {Hashtag_Stroke2_Corner0_Rounded as Hashtag} from '#/components/icons/Hashtag'
11// import {CloseQuote_Filled_Stroke2_Corner0_Rounded as Quote} from '#/components/icons/Quote'
12// import {UserAvatar} from '#/view/com/util/UserAvatar'
13import {type TrendingTopic} from '#/state/queries/trending/useTrendingTopics'
14import {atoms as a, native, useTheme, type ViewStyleProp} from '#/alf'
15import {StarterPack as StarterPackIcon} from '#/components/icons/StarterPack'
16import {Link as InternalLink, type LinkProps} from '#/components/Link'
17import {Text} from '#/components/Typography'
18
19export function TrendingTopic({
20 topic: raw,
21 size,
22 style,
23 hovered,
24}: {
25 topic: TrendingTopic
26 size?: 'large' | 'small'
27 hovered?: boolean
28} & ViewStyleProp) {
29 const topic = useTopic(raw)
30
31 const isSmall = size === 'small'
32 const hasIcon = topic.type === 'starter-pack' && !isSmall
33 const iconSize = 20
34
35 return (
36 <View
37 style={[
38 a.flex_row,
39 a.align_center,
40 isSmall
41 ? [
42 {
43 paddingVertical: 2,
44 paddingHorizontal: 4,
45 },
46 ]
47 : [a.py_xs, a.px_sm],
48 hasIcon && {gap: 6},
49 style,
50 ]}>
51 {hasIcon && topic.type === 'starter-pack' && (
52 <StarterPackIcon
53 gradient="sky"
54 width={iconSize}
55 style={{marginLeft: -3, marginVertical: -1}}
56 />
57 )}
58
59 {/*
60 <View
61 style={[
62 a.align_center,
63 a.justify_center,
64 a.rounded_full,
65 a.overflow_hidden,
66 {
67 width: iconSize,
68 height: iconSize,
69 },
70 ]}>
71 {topic.type === 'tag' ? (
72 <Hashtag width={iconSize} />
73 ) : topic.type === 'topic' ? (
74 <Quote width={iconSize - 2} />
75 ) : topic.type === 'feed' ? (
76 <UserAvatar
77 type="user"
78 size={aviSize}
79 avatar=""
80 />
81 ) : (
82 <UserAvatar
83 type="user"
84 size={aviSize}
85 avatar=""
86 />
87 )}
88 </View>
89 */}
90
91 <Text
92 style={[
93 a.font_semi_bold,
94 a.leading_tight,
95 isSmall ? [a.text_sm] : [a.text_md, {paddingBottom: 1}],
96 hovered && {textDecorationLine: 'underline'},
97 ]}
98 numberOfLines={1}>
99 {topic.displayName}
100 </Text>
101 </View>
102 )
103}
104
105export function TrendingTopicSkeleton({
106 size = 'large',
107 index = 0,
108}: {
109 size?: 'large' | 'small'
110 index?: number
111}) {
112 const t = useTheme()
113 const isSmall = size === 'small'
114 return (
115 <View
116 style={[
117 a.rounded_full,
118 a.border,
119 t.atoms.border_contrast_medium,
120 t.atoms.bg_contrast_25,
121 isSmall
122 ? {
123 width: index % 2 === 0 ? 75 : 90,
124 height: 27,
125 }
126 : {
127 width: index % 2 === 0 ? 90 : 110,
128 height: 36,
129 },
130 ]}
131 />
132 )
133}
134
135export function TrendingTopicLink({
136 topic: raw,
137 children,
138 ...rest
139}: {
140 topic: TrendingTopic
141} & Omit<LinkProps, 'to' | 'label'>) {
142 const topic = useTopic(raw)
143
144 return (
145 <InternalLink
146 label={topic.label}
147 to={topic.url}
148 PressableComponent={native(PressableScale)}
149 {...rest}>
150 {children}
151 </InternalLink>
152 )
153}
154
155type ParsedTrendingTopic =
156 | {
157 type: 'topic' | 'tag' | 'starter-pack' | 'unknown'
158 label: string
159 displayName: string
160 url: string
161 uri: undefined
162 }
163 | {
164 type: 'profile' | 'feed'
165 label: string
166 displayName: string
167 url: string
168 uri: AtUri
169 }
170
171export function useTopic(raw: TrendingTopic): ParsedTrendingTopic {
172 const {_} = useLingui()
173 return useMemo(() => {
174 const {topic: displayName, link} = raw
175
176 if (link.startsWith('/search')) {
177 return {
178 type: 'topic',
179 label: _(msg`Browse posts about ${displayName}`),
180 displayName,
181 uri: undefined,
182 url: link,
183 }
184 } else if (link.startsWith('/hashtag')) {
185 return {
186 type: 'tag',
187 label: _(msg`Browse posts tagged with ${displayName}`),
188 displayName,
189 // displayName: displayName.replace(/^#/, ''),
190 uri: undefined,
191 url: link,
192 }
193 } else if (link.startsWith('/starter-pack')) {
194 return {
195 type: 'starter-pack',
196 label: _(msg`Browse starter pack ${displayName}`),
197 displayName,
198 uri: undefined,
199 url: link,
200 }
201 }
202
203 /*
204 if (!link.startsWith('at://')) {
205 // above logic
206 } else {
207 const urip = new AtUri(link)
208 switch (urip.collection) {
209 case 'app.bsky.actor.profile': {
210 return {
211 type: 'profile',
212 label: _(msg`View ${displayName}'s profile`),
213 displayName,
214 uri: urip,
215 url: makeProfileLink({did: urip.host, handle: urip.host}),
216 }
217 }
218 case 'app.bsky.feed.generator': {
219 return {
220 type: 'feed',
221 label: _(msg`Browse the ${displayName} feed`),
222 displayName,
223 uri: urip,
224 url: feedUriToHref(link),
225 }
226 }
227 }
228 }
229 */
230
231 return {
232 type: 'unknown',
233 label: _(msg`Browse topic ${displayName}`),
234 displayName,
235 uri: undefined,
236 url: link,
237 }
238 }, [_, raw])
239}