Exploring new sidebar design

This commit is contained in:
Alex Benzer
2025-12-31 11:14:32 -08:00
committed by Samuel Newman
parent d5c9d83fb8
commit 6bcb5236d9
2 changed files with 185 additions and 64 deletions
+137 -35
View File
@@ -1,4 +1,4 @@
import {View} from 'react-native'
import {Pressable, View} from 'react-native'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useNavigation, useNavigationState} from '@react-navigation/native'
@@ -7,10 +7,18 @@ import {getCurrentRoute} from '#/lib/routes/helpers'
import {type NavigationProp} from '#/lib/routes/types'
import {logger} from '#/logger'
import {emitSoftReset} from '#/state/events'
import {usePinnedFeedsInfos} from '#/state/queries/feed'
import {
type SavedFeedSourceInfo,
usePinnedFeedsInfos,
} from '#/state/queries/feed'
import {useSelectedFeed, useSetSelectedFeed} from '#/state/shell/selected-feed'
import {UserAvatar} from '#/view/com/util/UserAvatar'
import {atoms as a, useTheme, web} from '#/alf'
import {createStaticClick, InlineLinkText} from '#/components/Link'
import {useInteractionState} from '#/components/hooks/useInteractionState'
import {FilterTimeline_Stroke2_Corner0_Rounded as FilterTimeline} from '#/components/icons/FilterTimeline'
import {PlusSmall_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
import {Link} from '#/components/Link'
import {Text} from '#/components/Typography'
export function DesktopFeeds() {
const t = useTheme()
@@ -57,13 +65,12 @@ export function DesktopFeeds() {
style={[
a.flex_1,
web({
gap: 10,
gap: 2,
/*
* Small padding prevents overflow prior to actually overflowing the
* height of the screen with lots of feeds.
*/
paddingVertical: 2,
marginHorizontal: -2,
overflowY: 'auto',
}),
]}>
@@ -72,10 +79,11 @@ export function DesktopFeeds() {
const current = route.name === 'Home' && feed === selectedFeed
return (
<InlineLinkText
<FeedItem
key={feedInfo.uri}
label={feedInfo.displayName}
{...createStaticClick(() => {
feedInfo={feedInfo}
current={current}
onPress={() => {
logger.metric(
'desktopFeeds:feed:click',
{
@@ -89,39 +97,133 @@ export function DesktopFeeds() {
if (route.name === 'Home' && feed === selectedFeed) {
emitSoftReset()
}
})}
style={[
a.text_md,
a.leading_snug,
a.flex_shrink_0,
current
? [a.font_semi_bold, t.atoms.text]
: [t.atoms.text_contrast_medium],
web({
marginHorizontal: 2,
width: 'calc(100% - 4px)',
}),
]}
numberOfLines={1}>
{feedInfo.displayName}
</InlineLinkText>
}}
/>
)
})}
<InlineLinkText
<Link
to="/feeds"
label={_(msg`More feeds`)}
style={[
a.text_md,
a.leading_snug,
web({
marginHorizontal: 2,
width: 'calc(100% - 4px)',
}),
]}
numberOfLines={1}>
{_(msg`More feeds`)}
</InlineLinkText>
a.flex_row,
a.align_center,
a.gap_sm,
a.self_start,
a.rounded_sm,
{paddingVertical: 6, paddingHorizontal: 8},
]}>
{({hovered}) => (
<>
<View
style={[
a.align_center,
a.justify_center,
a.rounded_xs,
t.atoms.bg_contrast_50,
{
width: 20,
height: 20,
},
]}>
<Plus
style={{width: 16, height: 16}}
fill={
hovered
? t.atoms.text.color
: t.atoms.text_contrast_medium.color
}
/>
</View>
<Text
style={[
a.text_md,
a.leading_snug,
hovered ? t.atoms.text : t.atoms.text_contrast_medium,
]}
numberOfLines={1}>
{_(msg`More feeds`)}
</Text>
</>
)}
</Link>
</View>
)
}
function FeedItem({
feedInfo,
current,
onPress,
}: {
feedInfo: SavedFeedSourceInfo
current: boolean
onPress: () => void
}) {
const t = useTheme()
const {
state: hovered,
onIn: onHoverIn,
onOut: onHoverOut,
} = useInteractionState()
const isFollowing = feedInfo.feedDescriptor === 'following'
return (
<Pressable
accessibilityRole="link"
accessibilityLabel={feedInfo.displayName}
accessibilityHint=""
onPress={onPress}
onHoverIn={onHoverIn}
onHoverOut={onHoverOut}
style={[
a.flex_row,
a.align_center,
a.gap_sm,
a.self_start,
a.rounded_sm,
{paddingVertical: 6, paddingHorizontal: 8},
// current && t.atoms.bg_contrast_50,
current && {backgroundColor: t.palette.primary_50},
]}>
{isFollowing ? (
<View
style={[
a.align_center,
a.justify_center,
a.rounded_xs,
{
width: 20,
height: 20,
backgroundColor: t.palette.primary_500,
},
]}>
<FilterTimeline
style={{width: 14, height: 14}}
fill={t.palette.white}
/>
</View>
) : (
<UserAvatar
type={feedInfo.type === 'list' ? 'list' : 'algo'}
size={20}
avatar={feedInfo.avatar}
noBorder
/>
)}
<Text
style={[
a.text_md,
a.leading_snug,
current
? [t.atoms.text, a.font_semi_bold]
: hovered
? t.atoms.text
: t.atoms.text_contrast_medium,
]}
numberOfLines={1}>
{feedInfo.displayName}
</Text>
</Pressable>
)
}
@@ -12,16 +12,13 @@ import {useTrendingTopics} from '#/state/queries/trending/useTrendingTopics'
import {useTrendingConfig} from '#/state/service-config'
import {atoms as a, useTheme} from '#/alf'
import {Button, ButtonIcon} from '#/components/Button'
import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
import {DotGrid_Stroke2_Corner0_Rounded as Ellipsis} from '#/components/icons/DotGrid'
import {Trending3_Stroke2_Corner1_Rounded as TrendingIcon} from '#/components/icons/Trending'
import * as Prompt from '#/components/Prompt'
import {
TrendingTopic,
TrendingTopicLink,
TrendingTopicSkeleton,
} from '#/components/TrendingTopics'
import {TrendingTopicLink} from '#/components/TrendingTopics'
import {Text} from '#/components/Typography'
const TRENDING_LIMIT = 6
const TRENDING_LIMIT = 5
export function SidebarTrendingTopics() {
const {enabled} = useTrendingConfig()
@@ -46,22 +43,17 @@ function Inner() {
<>
<View
style={[
a.gap_sm,
a.pt_sm,
a.pr_sm,
a.pb_md,
a.pl_md,
a.rounded_md,
t.atoms.bg_contrast_25,
a.border,
t.atoms.border_contrast_low,
]}>
<View style={[a.flex_row, a.align_center, a.gap_xs]}>
<Text
style={[
a.flex_1,
a.text_sm,
a.font_semi_bold,
t.atoms.text_contrast_medium,
]}>
<View style={[a.flex_row, a.align_center, a.gap_xs, a.pb_sm]}>
<TrendingIcon width={16} height={16} fill={t.atoms.text.color} />
<Text style={[a.flex_1, a.text_md, a.font_semi_bold, t.atoms.text]}>
<Trans>Trending</Trans>
</Text>
<Button
@@ -69,37 +61,64 @@ function Inner() {
size="tiny"
color="secondary"
shape="round"
label={_(msg`Hide trending topics`)}
label={_(msg`Trending options`)}
onPress={() => trendingPrompt.open()}
style={[a.bg_transparent]}>
<ButtonIcon icon={X} size="xs" />
<ButtonIcon icon={Ellipsis} size="xs" />
</Button>
</View>
<View
style={[a.flex_row, a.flex_wrap, {gap: '6px 4px', marginLeft: -4}]}>
<View style={[a.gap_sm]}>
{isLoading ? (
Array(TRENDING_LIMIT)
.fill(0)
.map((_n, i) => (
<TrendingTopicSkeleton key={i} size="small" index={i} />
<View key={i} style={[a.flex_row, a.align_center, a.gap_sm]}>
<Text
style={[
a.text_sm,
t.atoms.text_contrast_low,
{minWidth: 16},
]}>
{i + 1}.
</Text>
<View
style={[
a.rounded_xs,
t.atoms.bg_contrast_50,
{height: 14, width: i % 2 === 0 ? 80 : 100},
]}
/>
</View>
))
) : !trending?.topics ? null : (
<>
{trending.topics.slice(0, TRENDING_LIMIT).map(topic => (
{trending.topics.slice(0, TRENDING_LIMIT).map((topic, i) => (
<TrendingTopicLink
key={topic.link}
topic={topic}
style={a.rounded_full}
onPress={() => {
logEvent('trendingTopic:click', {context: 'sidebar'})
}}>
{({hovered}) => (
<TrendingTopic
size="small"
topic={topic}
hovered={hovered}
/>
<View style={[a.flex_row, a.align_center, a.gap_xs]}>
<Text
style={[
a.text_sm,
t.atoms.text_contrast_low,
{minWidth: 16},
]}>
{i + 1}.
</Text>
<Text
style={[
a.text_sm,
hovered ? t.atoms.text : t.atoms.text_contrast_medium,
]}
numberOfLines={1}>
{topic.displayName ?? topic.topic}
</Text>
</View>
)}
</TrendingTopicLink>
))}