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