mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
at samuel/exp-cli 238 lines 5.9 kB view raw
1import React from 'react' 2import {View} from 'react-native' 3import {AtUri} from '@atproto/api' 4import {msg} from '@lingui/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, ViewStyleProp} from '#/alf' 15import {StarterPack as StarterPackIcon} from '#/components/icons/StarterPack' 16import {Link as InternalLink, LinkProps} from '#/components/Link' 17import {Text} from '#/components/Typography' 18 19export function TrendingTopic({ 20 topic: raw, 21 size, 22 style, 23}: {topic: TrendingTopic; size?: 'large' | 'small'} & ViewStyleProp) { 24 const t = useTheme() 25 const topic = useTopic(raw) 26 27 const isSmall = size === 'small' 28 const hasIcon = topic.type === 'starter-pack' && !isSmall 29 const iconSize = 20 30 31 return ( 32 <View 33 style={[ 34 a.flex_row, 35 a.align_center, 36 a.rounded_full, 37 a.border, 38 t.atoms.border_contrast_medium, 39 t.atoms.bg, 40 isSmall 41 ? [ 42 { 43 paddingVertical: 5, 44 paddingHorizontal: 10, 45 }, 46 ] 47 : [a.py_sm, a.px_md], 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_bold, 94 a.leading_tight, 95 isSmall ? [a.text_sm] : [a.text_md, {paddingBottom: 1}], 96 ]} 97 numberOfLines={1}> 98 {topic.displayName} 99 </Text> 100 </View> 101 ) 102} 103 104export function TrendingTopicSkeleton({ 105 size = 'large', 106 index = 0, 107}: { 108 size?: 'large' | 'small' 109 index?: number 110}) { 111 const t = useTheme() 112 const isSmall = size === 'small' 113 return ( 114 <View 115 style={[ 116 a.rounded_full, 117 a.border, 118 t.atoms.border_contrast_medium, 119 t.atoms.bg_contrast_25, 120 isSmall 121 ? { 122 width: index % 2 === 0 ? 75 : 90, 123 height: 27, 124 } 125 : { 126 width: index % 2 === 0 ? 90 : 110, 127 height: 36, 128 }, 129 ]} 130 /> 131 ) 132} 133 134export function TrendingTopicLink({ 135 topic: raw, 136 children, 137 ...rest 138}: { 139 topic: TrendingTopic 140} & Omit<LinkProps, 'to' | 'label'>) { 141 const topic = useTopic(raw) 142 143 return ( 144 <InternalLink 145 label={topic.label} 146 to={topic.url} 147 PressableComponent={native(PressableScale)} 148 {...rest}> 149 {children} 150 </InternalLink> 151 ) 152} 153 154type ParsedTrendingTopic = 155 | { 156 type: 'topic' | 'tag' | 'starter-pack' | 'unknown' 157 label: string 158 displayName: string 159 url: string 160 uri: undefined 161 } 162 | { 163 type: 'profile' | 'feed' 164 label: string 165 displayName: string 166 url: string 167 uri: AtUri 168 } 169 170export function useTopic(raw: TrendingTopic): ParsedTrendingTopic { 171 const {_} = useLingui() 172 return React.useMemo(() => { 173 const {topic: displayName, link} = raw 174 175 if (link.startsWith('/search')) { 176 return { 177 type: 'topic', 178 label: _(msg`Browse posts about ${displayName}`), 179 displayName, 180 uri: undefined, 181 url: link, 182 } 183 } else if (link.startsWith('/hashtag')) { 184 return { 185 type: 'tag', 186 label: _(msg`Browse posts tagged with ${displayName}`), 187 displayName, 188 // displayName: displayName.replace(/^#/, ''), 189 uri: undefined, 190 url: link, 191 } 192 } else if (link.startsWith('/starter-pack')) { 193 return { 194 type: 'starter-pack', 195 label: _(msg`Browse starter pack ${displayName}`), 196 displayName, 197 uri: undefined, 198 url: link, 199 } 200 } 201 202 /* 203 if (!link.startsWith('at://')) { 204 // above logic 205 } else { 206 const urip = new AtUri(link) 207 switch (urip.collection) { 208 case 'app.bsky.actor.profile': { 209 return { 210 type: 'profile', 211 label: _(msg`View ${displayName}'s profile`), 212 displayName, 213 uri: urip, 214 url: makeProfileLink({did: urip.host, handle: urip.host}), 215 } 216 } 217 case 'app.bsky.feed.generator': { 218 return { 219 type: 'feed', 220 label: _(msg`Browse the ${displayName} feed`), 221 displayName, 222 uri: urip, 223 url: feedUriToHref(link), 224 } 225 } 226 } 227 } 228 */ 229 230 return { 231 type: 'unknown', 232 label: _(msg`Browse topic ${displayName}`), 233 displayName, 234 uri: undefined, 235 url: link, 236 } 237 }, [_, raw]) 238}