diff options
author | Eric Bailey <git@esb.lol> | 2024-12-17 21:45:39 -0600 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-12-17 19:45:39 -0800 |
commit | a2019aceec001e276272832b97ea5e2ec864c8a5 (patch) | |
tree | eaddab8a7a009650d93bb3b49c750619d98bb44d /src/components/TrendingTopics.tsx | |
parent | a07949ec8e63bae178a829f65c33fcd9622b28ec (diff) | |
download | voidsky-a2019aceec001e276272832b97ea5e2ec864c8a5.tar.zst |
Trending (Beta) (#7144)
* Add WIP UIs for trending topics and suggested starterpacks * Disable SPs for now * Improve explore treatment a bit, add some polish to cards * Add tiny option in RightNav * Add persisted option to hide trending from sidebar * Add to settings, abstract state, not updating in tab * Fix up hide/show toggle state, WITH broadcast hacK * Clean up persisted code, add new setting * Add new interstitial to Discover * Exploration * First hack at mute words * Wire up interstitial and Explore page * Align components * Some skeleton UI * Handle service config, enablement, load states, update lex contract * Centralize mute word handling * Stale time to 30m * Cache enabled value for reloads, use real data for service config * Remove broadcast hack * Remove titleChild * Gate settings too * Update package, rm langs * Add feature gate * Only english during beta period * Hook up real data * Tweak config * Straight passthrough links * Hook up prod agent * Fix no-show logic * Up config query to 5 min * Remove old file * Remove comment * Remove stray flex_1 * Make trending setting global * Quick placeholder state * Limit # in sidebar, tweak spacing * Tweak gaps * Handle hide/show of sidebar * Simplify messages * Remove interstitial * Revert "Remove interstitial" This reverts commit 1358ad47fdf7e633749340c410933b508af46c10. * Only show interstitial on mobile * Fix gap * Add explore page recommendations * [topics] add topic screen (#7149) * add topic screen * decode * fix search query * decode * add server route * Fix potential bad destructure (undefined) --------- Co-authored-by: Paul Frazee <pfrazee@gmail.com> Co-authored-by: Dan Abramov <dan.abramov@gmail.com> Co-authored-by: Hailey <me@haileyok.com>
Diffstat (limited to 'src/components/TrendingTopics.tsx')
-rw-r--r-- | src/components/TrendingTopics.tsx | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/src/components/TrendingTopics.tsx b/src/components/TrendingTopics.tsx new file mode 100644 index 000000000..6881f24bd --- /dev/null +++ b/src/components/TrendingTopics.tsx @@ -0,0 +1,223 @@ +import React from 'react' +import {View} from 'react-native' +import {AtUri} from '@atproto/api' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +// import {makeProfileLink} from '#/lib/routes/links' +// import {feedUriToHref} from '#/lib/strings/url-helpers' +// import {Hashtag_Stroke2_Corner0_Rounded as Hashtag} from '#/components/icons/Hashtag' +// import {CloseQuote_Filled_Stroke2_Corner0_Rounded as Quote} from '#/components/icons/Quote' +// import {UserAvatar} from '#/view/com/util/UserAvatar' +import type {TrendingTopic} from '#/state/queries/trending/useTrendingTopics' +import {atoms as a, useTheme, ViewStyleProp} from '#/alf' +import {Link as InternalLink, LinkProps} from '#/components/Link' +import {Text} from '#/components/Typography' + +export function TrendingTopic({ + topic: raw, + size, + style, +}: {topic: TrendingTopic; size?: 'large' | 'small'} & ViewStyleProp) { + const t = useTheme() + const topic = useTopic(raw) + + const isSmall = size === 'small' + // const hasAvi = topic.type === 'feed' || topic.type === 'profile' + // const aviSize = isSmall ? 16 : 20 + // const iconSize = isSmall ? 16 : 20 + + return ( + <View + style={[ + a.flex_row, + a.align_center, + a.rounded_full, + a.border, + t.atoms.border_contrast_medium, + t.atoms.bg, + isSmall + ? [ + { + paddingVertical: 5, + paddingHorizontal: 10, + }, + ] + : [a.py_sm, a.px_md], + style, + /* + { + padding: 6, + gap: hasAvi ? 4 : 2, + }, + a.pr_md, + */ + ]}> + {/* + <View + style={[ + a.align_center, + a.justify_center, + a.rounded_full, + a.overflow_hidden, + { + width: aviSize, + height: aviSize, + }, + ]}> + {topic.type === 'tag' ? ( + <Hashtag width={iconSize} /> + ) : topic.type === 'topic' ? ( + <Quote width={iconSize - 2} /> + ) : topic.type === 'feed' ? ( + <UserAvatar + type="user" + size={aviSize} + avatar="" + /> + ) : ( + <UserAvatar + type="user" + size={aviSize} + avatar="" + /> + )} + </View> + */} + + <Text + style={[ + a.font_bold, + a.leading_tight, + isSmall ? [a.text_sm] : [a.text_md, {paddingBottom: 1}], + ]} + numberOfLines={1}> + {topic.displayName} + </Text> + </View> + ) +} + +export function TrendingTopicSkeleton({ + size = 'large', + index = 0, +}: { + size?: 'large' | 'small' + index?: number +}) { + const t = useTheme() + const isSmall = size === 'small' + return ( + <View + style={[ + a.rounded_full, + a.border, + t.atoms.border_contrast_medium, + t.atoms.bg_contrast_25, + isSmall + ? { + width: index % 2 === 0 ? 75 : 90, + height: 27, + } + : { + width: index % 2 === 0 ? 90 : 110, + height: 36, + }, + ]} + /> + ) +} + +export function TrendingTopicLink({ + topic: raw, + children, + ...rest +}: { + topic: TrendingTopic +} & Omit<LinkProps, 'to' | 'label'>) { + const topic = useTopic(raw) + + return ( + <InternalLink label={topic.label} to={topic.url} {...rest}> + {children} + </InternalLink> + ) +} + +type ParsedTrendingTopic = + | { + type: 'topic' | 'tag' | 'unknown' + label: string + displayName: string + url: string + uri: undefined + } + | { + type: 'profile' | 'feed' + label: string + displayName: string + url: string + uri: AtUri + } + +export function useTopic(raw: TrendingTopic): ParsedTrendingTopic { + const {_} = useLingui() + return React.useMemo(() => { + const {topic: displayName, link} = raw + + if (link.startsWith('/search')) { + return { + type: 'topic', + label: _(msg`Browse posts about ${displayName}`), + displayName, + uri: undefined, + url: link, + } + } else if (link.startsWith('/hashtag')) { + return { + type: 'tag', + label: _(msg`Browse posts tagged with ${displayName}`), + displayName, + // displayName: displayName.replace(/^#/, ''), + uri: undefined, + url: link, + } + } + + /* + if (!link.startsWith('at://')) { + // above logic + } else { + const urip = new AtUri(link) + switch (urip.collection) { + case 'app.bsky.actor.profile': { + return { + type: 'profile', + label: _(msg`View ${displayName}'s profile`), + displayName, + uri: urip, + url: makeProfileLink({did: urip.host, handle: urip.host}), + } + } + case 'app.bsky.feed.generator': { + return { + type: 'feed', + label: _(msg`Browse the ${displayName} feed`), + displayName, + uri: urip, + url: feedUriToHref(link), + } + } + } + } + */ + + return { + type: 'unknown', + label: _(msg`Browse topic ${displayName}`), + displayName, + uri: undefined, + url: link, + } + }, [_, raw]) +} |