Update trending topics UI (#11304)
This commit is contained in:
@@ -596,11 +596,6 @@
|
|||||||
"count": 1
|
"count": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"src/components/interstitials/TrendingVideos.tsx": {
|
|
||||||
"typescript/no-floating-promises": {
|
|
||||||
"count": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"src/components/verification/VerificationCreatePrompt.tsx": {
|
"src/components/verification/VerificationCreatePrompt.tsx": {
|
||||||
"typescript/no-misused-promises": {
|
"typescript/no-misused-promises": {
|
||||||
"count": 1
|
"count": 1
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import {
|
|||||||
atoms as a,
|
atoms as a,
|
||||||
native,
|
native,
|
||||||
useBreakpoints,
|
useBreakpoints,
|
||||||
|
useGutters,
|
||||||
useTheme,
|
useTheme,
|
||||||
type ViewStyleProp,
|
type ViewStyleProp,
|
||||||
web,
|
web,
|
||||||
@@ -60,10 +61,10 @@ function CardOuter({
|
|||||||
a.flex_1,
|
a.flex_1,
|
||||||
a.w_full,
|
a.w_full,
|
||||||
a.p_md,
|
a.p_md,
|
||||||
a.rounded_lg,
|
a.rounded_xl,
|
||||||
a.border,
|
a.border,
|
||||||
t.atoms.bg,
|
t.atoms.bg,
|
||||||
t.atoms.shadow_sm,
|
t.atoms.shadow_md,
|
||||||
t.atoms.border_contrast_low,
|
t.atoms.border_contrast_low,
|
||||||
!gtMobile && {
|
!gtMobile && {
|
||||||
width: MOBILE_CARD_WIDTH,
|
width: MOBILE_CARD_WIDTH,
|
||||||
@@ -204,6 +205,7 @@ export function ProfileGrid({
|
|||||||
const {t: l} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const moderationOpts = useModerationOpts()
|
const moderationOpts = useModerationOpts()
|
||||||
const {gtMobile} = useBreakpoints()
|
const {gtMobile} = useBreakpoints()
|
||||||
|
const gutters = useGutters([0, 'base'])
|
||||||
const followDialogControl = useDialogControl()
|
const followDialogControl = useDialogControl()
|
||||||
|
|
||||||
const isLoading = isSuggestionsLoading || !moderationOpts
|
const isLoading = isSuggestionsLoading || !moderationOpts
|
||||||
@@ -414,7 +416,7 @@ export function ProfileGrid({
|
|||||||
moderationOpts={moderationOpts}
|
moderationOpts={moderationOpts}
|
||||||
logContext="FeedInterstitial"
|
logContext="FeedInterstitial"
|
||||||
withIcon={false}
|
withIcon={false}
|
||||||
style={[a.rounded_sm]}
|
style={[a.rounded_full]}
|
||||||
onFollow={() => {
|
onFollow={() => {
|
||||||
ax.metric('suggestedUser:follow', {
|
ax.metric('suggestedUser:follow', {
|
||||||
logContext,
|
logContext,
|
||||||
@@ -458,40 +460,43 @@ export function ProfileGrid({
|
|||||||
pointerEvents={IS_IOS ? 'auto' : 'box-none'}>
|
pointerEvents={IS_IOS ? 'auto' : 'box-none'}>
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
a.px_lg,
|
gutters,
|
||||||
a.pt_md,
|
a.pt_md,
|
||||||
a.flex_row,
|
a.flex_row,
|
||||||
a.align_center,
|
a.align_center,
|
||||||
a.justify_between,
|
a.justify_between,
|
||||||
]}
|
]}
|
||||||
pointerEvents={IS_IOS ? 'auto' : 'box-none'}>
|
pointerEvents={IS_IOS ? 'auto' : 'box-none'}>
|
||||||
<Text style={[a.text_sm, a.font_semi_bold, t.atoms.text]}>
|
<View style={[a.w_full, a.pl_xs, a.flex_row, a.align_center]}>
|
||||||
<Trans>Suggested for you</Trans>
|
<Text style={[a.flex_1, a.text_md, a.font_semi_bold]}>
|
||||||
</Text>
|
<Trans>Suggested for you</Trans>
|
||||||
<Button
|
</Text>
|
||||||
label={l`See more suggested profiles`}
|
<Button
|
||||||
onPress={() => {
|
label={l`See more suggested profiles`}
|
||||||
followDialogControl.open()
|
onPress={() => {
|
||||||
ax.metric('suggestedUser:seeMore', {
|
followDialogControl.open()
|
||||||
logContext,
|
ax.metric('suggestedUser:seeMore', {
|
||||||
recId,
|
logContext,
|
||||||
})
|
recId,
|
||||||
}}>
|
})
|
||||||
{({hovered}) => (
|
}}>
|
||||||
<Text
|
{({hovered, pressed}) => (
|
||||||
style={[
|
<Text
|
||||||
a.text_sm,
|
style={[
|
||||||
t.atoms.text_link,
|
a.text_sm,
|
||||||
hovered &&
|
a.font_medium,
|
||||||
web({
|
{
|
||||||
textDecorationLine: 'underline',
|
color:
|
||||||
textDecorationColor: t.atoms.text_link.color,
|
hovered || pressed
|
||||||
}),
|
? t.palette.contrast_800
|
||||||
]}>
|
: t.palette.contrast_500,
|
||||||
<Trans>See more</Trans>
|
},
|
||||||
</Text>
|
]}>
|
||||||
)}
|
<Trans>See more</Trans>
|
||||||
</Button>
|
</Text>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<FollowDialogWithoutGuide control={followDialogControl} />
|
<FollowDialogWithoutGuide control={followDialogControl} />
|
||||||
<LayoutAnimationConfig skipExiting skipEntering>
|
<LayoutAnimationConfig skipExiting skipEntering>
|
||||||
@@ -540,7 +545,7 @@ function SeeMoreSuggestedProfilesCard({onPress}: {onPress: () => void}) {
|
|||||||
a.justify_center,
|
a.justify_center,
|
||||||
a.gap_sm,
|
a.gap_sm,
|
||||||
a.p_md,
|
a.p_md,
|
||||||
a.rounded_lg,
|
a.rounded_xl,
|
||||||
{width: FINAL_CARD_WIDTH},
|
{width: FINAL_CARD_WIDTH},
|
||||||
]}>
|
]}>
|
||||||
<ButtonIcon icon={ArrowRight} size="lg" />
|
<ButtonIcon icon={ArrowRight} size="lg" />
|
||||||
|
|||||||
@@ -601,7 +601,7 @@ export function FollowButtonPlaceholder({style}: ViewStyleProp) {
|
|||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
a.rounded_sm,
|
a.rounded_full,
|
||||||
t.atoms.bg_contrast_50,
|
t.atoms.bg_contrast_50,
|
||||||
a.w_full,
|
a.w_full,
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ import {
|
|||||||
AppBskyFeedPost,
|
AppBskyFeedPost,
|
||||||
type ModerationDecision,
|
type ModerationDecision,
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
|
|
||||||
import {sanitizeHandle} from '#/lib/strings/handles'
|
import {sanitizeHandle} from '#/lib/strings/handles'
|
||||||
import {formatCount} from '#/view/com/util/numeric/format'
|
import {formatCount} from '#/view/com/util/numeric/format'
|
||||||
@@ -52,7 +51,7 @@ export function VideoPostCard({
|
|||||||
onInteract?: () => void
|
onInteract?: () => void
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_, i18n} = useLingui()
|
const {t: l, i18n} = useLingui()
|
||||||
const embed = post.embed
|
const embed = post.embed
|
||||||
const {
|
const {
|
||||||
state: pressed,
|
state: pressed,
|
||||||
@@ -118,8 +117,8 @@ export function VideoPostCard({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
accessibilityHint={_(msg`Views video in immersive mode`)}
|
accessibilityHint={l`Views video in immersive mode`}
|
||||||
label={_(msg`Video from ${author.handle}: ${text}`)}
|
label={l`Video from ${author.handle}: ${text}`}
|
||||||
to={{
|
to={{
|
||||||
screen: 'VideoFeed',
|
screen: 'VideoFeed',
|
||||||
params: {
|
params: {
|
||||||
@@ -174,9 +173,7 @@ export function VideoPostCard({
|
|||||||
/>
|
/>
|
||||||
<View style={[a.align_center, a.gap_xs]}>
|
<View style={[a.align_center, a.gap_xs]}>
|
||||||
<Eye size="lg" fill="white" />
|
<Eye size="lg" fill="white" />
|
||||||
<Text style={[a.text_sm, {color: 'white'}]}>
|
<Text style={[a.text_sm, {color: 'white'}]}>{l`Hidden`}</Text>
|
||||||
{_(msg`Hidden`)}
|
|
||||||
</Text>
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
@@ -367,7 +364,7 @@ export function CompactVideoPostCard({
|
|||||||
onInteract?: () => void
|
onInteract?: () => void
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_, i18n} = useLingui()
|
const {t: l, i18n} = useLingui()
|
||||||
const embed = post.embed
|
const embed = post.embed
|
||||||
const {
|
const {
|
||||||
state: pressed,
|
state: pressed,
|
||||||
@@ -398,7 +395,7 @@ export function CompactVideoPostCard({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
label={_(msg`View video`)}
|
label={l`View video`}
|
||||||
to={{
|
to={{
|
||||||
screen: 'VideoFeed',
|
screen: 'VideoFeed',
|
||||||
params: {
|
params: {
|
||||||
@@ -413,7 +410,8 @@ export function CompactVideoPostCard({
|
|||||||
onPressOut={onPressOut}
|
onPressOut={onPressOut}
|
||||||
style={[
|
style={[
|
||||||
a.flex_col,
|
a.flex_col,
|
||||||
t.atoms.shadow_sm,
|
a.rounded_xl,
|
||||||
|
t.atoms.shadow_md,
|
||||||
{
|
{
|
||||||
alignItems: undefined,
|
alignItems: undefined,
|
||||||
justifyContent: undefined,
|
justifyContent: undefined,
|
||||||
@@ -424,7 +422,7 @@ export function CompactVideoPostCard({
|
|||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
a.justify_center,
|
a.justify_center,
|
||||||
a.rounded_lg,
|
a.rounded_xl,
|
||||||
a.overflow_hidden,
|
a.overflow_hidden,
|
||||||
a.border,
|
a.border,
|
||||||
t.atoms.border_contrast_low,
|
t.atoms.border_contrast_low,
|
||||||
@@ -458,9 +456,7 @@ export function CompactVideoPostCard({
|
|||||||
/>
|
/>
|
||||||
<View style={[a.align_center, a.gap_xs]}>
|
<View style={[a.align_center, a.gap_xs]}>
|
||||||
<Eye size="lg" fill="white" />
|
<Eye size="lg" fill="white" />
|
||||||
<Text style={[a.text_sm, {color: 'white'}]}>
|
<Text style={[a.text_sm, {color: 'white'}]}>{l`Hidden`}</Text>
|
||||||
{_(msg`Hidden`)}
|
|
||||||
</Text>
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
@@ -469,7 +465,7 @@ export function CompactVideoPostCard({
|
|||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
a.justify_center,
|
a.justify_center,
|
||||||
a.rounded_lg,
|
a.rounded_xl,
|
||||||
a.overflow_hidden,
|
a.overflow_hidden,
|
||||||
a.border,
|
a.border,
|
||||||
t.atoms.border_contrast_low,
|
t.atoms.border_contrast_low,
|
||||||
@@ -485,10 +481,17 @@ export function CompactVideoPostCard({
|
|||||||
/>
|
/>
|
||||||
<MediaInsetBorder />
|
<MediaInsetBorder />
|
||||||
|
|
||||||
<View style={[a.absolute, a.inset_0, t.atoms.shadow_sm]}>
|
<View style={[a.absolute, a.inset_0, t.atoms.shadow_md]}>
|
||||||
<View style={[a.absolute, a.inset_0, a.p_sm, {bottom: 'auto'}]}>
|
<View style={[a.absolute, a.inset_0, a.p_sm, {bottom: 'auto'}]}>
|
||||||
<View
|
<View
|
||||||
style={[a.relative, a.rounded_full, {width: 24, height: 24}]}>
|
style={[
|
||||||
|
a.relative,
|
||||||
|
a.rounded_full,
|
||||||
|
{
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
},
|
||||||
|
]}>
|
||||||
<UserAvatar
|
<UserAvatar
|
||||||
type="user"
|
type="user"
|
||||||
size={24}
|
size={24}
|
||||||
@@ -547,10 +550,10 @@ export function CompactVideoPostCardPlaceholder() {
|
|||||||
const black = getBlackColor(t)
|
const black = getBlackColor(t)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[a.flex_1, t.atoms.shadow_sm]}>
|
<View style={[a.flex_1, t.atoms.shadow_md]}>
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
a.rounded_lg,
|
a.rounded_xl,
|
||||||
a.overflow_hidden,
|
a.overflow_hidden,
|
||||||
a.border,
|
a.border,
|
||||||
t.atoms.border_contrast_low,
|
t.atoms.border_contrast_low,
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ import {type AppBskyUnspeccedDefs, moderateProfile} from '@atproto/api'
|
|||||||
import {Plural, Trans, useLingui} from '@lingui/react/macro'
|
import {Plural, Trans, useLingui} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||||
import {useTrendingSettings} from '#/state/preferences/trending'
|
import {
|
||||||
|
useTrendingSettings,
|
||||||
|
useTrendingSettingsApi,
|
||||||
|
} from '#/state/preferences/trending'
|
||||||
import {useGetTrendsQuery} from '#/state/queries/trending/useGetTrendsQuery'
|
import {useGetTrendsQuery} from '#/state/queries/trending/useGetTrendsQuery'
|
||||||
import {useTrendingConfig} from '#/state/service-config'
|
import {useTrendingConfig} from '#/state/service-config'
|
||||||
import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
|
import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
|
||||||
@@ -16,10 +19,12 @@ import {
|
|||||||
useTheme,
|
useTheme,
|
||||||
type ViewStyleProp,
|
type ViewStyleProp,
|
||||||
} from '#/alf'
|
} from '#/alf'
|
||||||
import {alpha} from '#/alf/utils'
|
|
||||||
import {AvatarStack} from '#/components/AvatarStack'
|
import {AvatarStack} from '#/components/AvatarStack'
|
||||||
|
import {Button, ButtonIcon} from '#/components/Button'
|
||||||
|
import {DotGrid3x1_Stroke2_Corner0_Rounded as EllipsisIcon} from '#/components/icons/DotGrid'
|
||||||
import {Trending3_Stroke2_Corner1_Rounded as TrendingIcon} from '#/components/icons/Trending'
|
import {Trending3_Stroke2_Corner1_Rounded as TrendingIcon} from '#/components/icons/Trending'
|
||||||
import {Link} from '#/components/Link'
|
import {Link} from '#/components/Link'
|
||||||
|
import * as Prompt from '#/components/Prompt'
|
||||||
import {SubtleHover} from '#/components/SubtleHover'
|
import {SubtleHover} from '#/components/SubtleHover'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
import {useAnalytics} from '#/analytics'
|
import {useAnalytics} from '#/analytics'
|
||||||
@@ -39,6 +44,8 @@ function Inner() {
|
|||||||
const {t: l} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const gutters = useGutters([0, 'base'])
|
const gutters = useGutters([0, 'base'])
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
|
const trendingPrompt = Prompt.usePromptControl()
|
||||||
|
const {setTrendingDisabled} = useTrendingSettingsApi()
|
||||||
const {
|
const {
|
||||||
data: trending,
|
data: trending,
|
||||||
error,
|
error,
|
||||||
@@ -47,90 +54,109 @@ function Inner() {
|
|||||||
} = useGetTrendsQuery({limit: TOPIC_COUNT})
|
} = useGetTrendsQuery({limit: TOPIC_COUNT})
|
||||||
const noTopics = !isLoading && !error && !trending?.trends?.length
|
const noTopics = !isLoading && !error && !trending?.trends?.length
|
||||||
|
|
||||||
const shadowColor = alpha(t.palette.primary_100, 0.5)
|
|
||||||
|
|
||||||
if (error || noTopics) {
|
if (error || noTopics) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<>
|
||||||
style={[
|
|
||||||
gutters,
|
|
||||||
a.pt_lg,
|
|
||||||
a.pb_xl,
|
|
||||||
a.gap_sm,
|
|
||||||
a.border_t,
|
|
||||||
t.atoms.border_contrast_low,
|
|
||||||
]}>
|
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
a.relative,
|
gutters,
|
||||||
a.z_20,
|
a.pt_xs,
|
||||||
a.px_xs,
|
a.pb_lg,
|
||||||
a.flex_row,
|
a.gap_xs,
|
||||||
a.align_center,
|
a.border_t,
|
||||||
a.justify_between,
|
t.atoms.border_contrast_low,
|
||||||
a.gap_sm,
|
t.atoms.bg_contrast_25,
|
||||||
]}>
|
]}>
|
||||||
<View style={[a.flex_row, a.align_center, a.justify_between, a.gap_xs]}>
|
<View
|
||||||
<TrendingIcon width={18} />
|
style={[
|
||||||
<Text style={[a.text_md, a.font_medium]} numberOfLines={1}>
|
a.relative,
|
||||||
<Trans>Trending</Trans>
|
a.z_20,
|
||||||
</Text>
|
a.pl_xs,
|
||||||
</View>
|
a.flex_row,
|
||||||
<Link label={l`See more trending topics`} to="/search">
|
a.align_center,
|
||||||
{({hovered, pressed}) => (
|
a.justify_between,
|
||||||
<Text
|
a.gap_sm,
|
||||||
style={[
|
]}>
|
||||||
a.text_sm,
|
<View
|
||||||
a.font_medium,
|
style={[a.flex_row, a.align_center, a.justify_between, a.gap_xs]}>
|
||||||
{
|
<TrendingIcon width={18} fill={t.atoms.text.color} />
|
||||||
color:
|
<Text style={[a.text_md, a.font_medium]} numberOfLines={1}>
|
||||||
hovered || pressed
|
<Trans>Trending</Trans>
|
||||||
? t.palette.contrast_800
|
|
||||||
: t.palette.contrast_500,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
numberOfLines={1}>
|
|
||||||
<Trans>See more</Trans>
|
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
</View>
|
||||||
</Link>
|
<View style={[a.flex_row, a.align_center, a.gap_xs]}>
|
||||||
|
<Link label={l`See more trending topics`} to="/search">
|
||||||
|
{({hovered, pressed}) => (
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
a.text_sm,
|
||||||
|
a.font_medium,
|
||||||
|
{
|
||||||
|
color:
|
||||||
|
hovered || pressed
|
||||||
|
? t.palette.contrast_800
|
||||||
|
: t.palette.contrast_500,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
numberOfLines={1}>
|
||||||
|
<Trans>See more</Trans>
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Link>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="medium"
|
||||||
|
color="secondary"
|
||||||
|
shape="round"
|
||||||
|
label={l`Trending options`}
|
||||||
|
onPress={() => trendingPrompt.open()}
|
||||||
|
style={[a.bg_transparent]}>
|
||||||
|
<ButtonIcon icon={EllipsisIcon} size="md" />
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View style={[a.relative, a.z_10, a.rounded_xl, t.atoms.shadow_md]}>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.overflow_hidden,
|
||||||
|
a.border,
|
||||||
|
a.rounded_xl,
|
||||||
|
t.atoms.bg,
|
||||||
|
t.atoms.border_contrast_low,
|
||||||
|
]}>
|
||||||
|
{isLoading || isRefetching
|
||||||
|
? Array.from({length: TOPIC_COUNT}).map((_, i) => (
|
||||||
|
<TrendingTopicRowSkeleton key={i} rank={i + 1} />
|
||||||
|
))
|
||||||
|
: trending?.trends?.map((trend, index) => (
|
||||||
|
<TrendRow
|
||||||
|
key={trend.link}
|
||||||
|
trend={trend}
|
||||||
|
rank={index + 1}
|
||||||
|
onPress={() => {
|
||||||
|
ax.metric('trendingTopic:click', {
|
||||||
|
context: 'interstitial',
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<View
|
<Prompt.Basic
|
||||||
style={[
|
control={trendingPrompt}
|
||||||
a.relative,
|
title={l`Hide trending topics?`}
|
||||||
a.z_10,
|
description={l`You can update this later from your settings.`}
|
||||||
a.border,
|
confirmButtonCta={l`Hide`}
|
||||||
a.rounded_xl,
|
onConfirm={() => {
|
||||||
t.atoms.bg,
|
ax.metric('trendingTopics:hide', {context: 'interstitial'})
|
||||||
{
|
setTrendingDisabled(true)
|
||||||
borderColor: t.palette.primary_100,
|
}}
|
||||||
boxShadow: `0 0 16px 0 ${shadowColor}`,
|
/>
|
||||||
elevation: 8,
|
</>
|
||||||
shadowColor: shadowColor,
|
|
||||||
shadowOffset: {width: 0, height: 0},
|
|
||||||
shadowOpacity: 1,
|
|
||||||
shadowRadius: 16,
|
|
||||||
},
|
|
||||||
]}>
|
|
||||||
{isLoading || isRefetching
|
|
||||||
? Array.from({length: TOPIC_COUNT}).map((_, i) => (
|
|
||||||
<TrendingTopicRowSkeleton key={i} rank={i + 1} />
|
|
||||||
))
|
|
||||||
: trending?.trends?.map((trend, index) => (
|
|
||||||
<TrendRow
|
|
||||||
key={trend.link}
|
|
||||||
trend={trend}
|
|
||||||
rank={index + 1}
|
|
||||||
onPress={() => {
|
|
||||||
ax.metric('trendingTopic:click', {context: 'interstitial'})
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,12 +182,7 @@ function TrendRow({
|
|||||||
label={l`Browse topic ${trend.displayName}`}
|
label={l`Browse topic ${trend.displayName}`}
|
||||||
to={trend.link}
|
to={trend.link}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
style={[
|
style={[rank < TOPIC_COUNT && a.border_b, t.atoms.border_contrast_low]}
|
||||||
rank < TOPIC_COUNT && a.border_b,
|
|
||||||
{
|
|
||||||
borderColor: t.palette.primary_100,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
PressableComponent={Pressable}>
|
PressableComponent={Pressable}>
|
||||||
{({hovered, pressed}) => (
|
{({hovered, pressed}) => (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import {useCallback, useEffect, useMemo} from 'react'
|
import {useCallback, useEffect, useMemo} from 'react'
|
||||||
import {ScrollView, View} from 'react-native'
|
import {ScrollView, View} from 'react-native'
|
||||||
import {AppBskyEmbedVideo, AtUri} from '@atproto/api'
|
import {AppBskyEmbedVideo, AtUri} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {Trans, useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
|
||||||
import {useQueryClient} from '@tanstack/react-query'
|
import {useQueryClient} from '@tanstack/react-query'
|
||||||
|
|
||||||
import {VIDEO_FEED_URI} from '#/lib/constants'
|
import {VIDEO_FEED_URI} from '#/lib/constants'
|
||||||
@@ -14,7 +12,7 @@ import {BlockDrawerGesture} from '#/view/shell/BlockDrawerGesture'
|
|||||||
import {atoms as a, useGutters, useTheme} from '#/alf'
|
import {atoms as a, useGutters, useTheme} from '#/alf'
|
||||||
import {Button, ButtonIcon} from '#/components/Button'
|
import {Button, ButtonIcon} from '#/components/Button'
|
||||||
import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron'
|
import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron'
|
||||||
import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
|
import {DotGrid3x1_Stroke2_Corner0_Rounded as EllipsisIcon} from '#/components/icons/DotGrid'
|
||||||
import {Link} from '#/components/Link'
|
import {Link} from '#/components/Link'
|
||||||
import * as Prompt from '#/components/Prompt'
|
import * as Prompt from '#/components/Prompt'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
@@ -35,7 +33,7 @@ const FEED_PARAMS: {
|
|||||||
|
|
||||||
export function TrendingVideos() {
|
export function TrendingVideos() {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const gutters = useGutters([0, 'base'])
|
const gutters = useGutters([0, 'base'])
|
||||||
const {data, isLoading, error} = usePostFeedQuery(FEED_DESC, FEED_PARAMS)
|
const {data, isLoading, error} = usePostFeedQuery(FEED_DESC, FEED_PARAMS)
|
||||||
@@ -48,7 +46,7 @@ export function TrendingVideos() {
|
|||||||
.getQueryCache()
|
.getQueryCache()
|
||||||
.find({queryKey: RQKEY(FEED_DESC, FEED_PARAMS)})
|
.find({queryKey: RQKEY(FEED_DESC, FEED_PARAMS)})
|
||||||
if (query && query.getObserversCount() <= 1) {
|
if (query && query.getObserversCount() <= 1) {
|
||||||
query.fetch()
|
void query.fetch()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [queryClient])
|
}, [queryClient])
|
||||||
@@ -83,20 +81,22 @@ export function TrendingVideos() {
|
|||||||
a.align_center,
|
a.align_center,
|
||||||
a.justify_between,
|
a.justify_between,
|
||||||
]}>
|
]}>
|
||||||
<Text style={[a.text_sm, a.font_semi_bold, a.leading_snug]}>
|
<View style={[a.pl_xs, a.flex_row, a.align_center]}>
|
||||||
<Trans>Trending Videos</Trans>
|
<Text style={[a.flex_1, a.text_md, a.font_semi_bold]}>
|
||||||
</Text>
|
<Trans>Trending videos</Trans>
|
||||||
<Button
|
</Text>
|
||||||
label={_(msg`Dismiss this section`)}
|
<Button
|
||||||
size="tiny"
|
label={l`Dismiss this section`}
|
||||||
variant="solid"
|
size="small"
|
||||||
color="secondary"
|
variant="ghost"
|
||||||
shape="square"
|
color="secondary"
|
||||||
onPress={() => trendingPrompt.open()}>
|
shape="round"
|
||||||
<ButtonIcon icon={X} size="sm" />
|
style={[a.bg_transparent]}
|
||||||
</Button>
|
onPress={() => trendingPrompt.open()}>
|
||||||
|
<ButtonIcon icon={EllipsisIcon} size="md" />
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<BlockDrawerGesture>
|
<BlockDrawerGesture>
|
||||||
<ScrollView
|
<ScrollView
|
||||||
horizontal
|
horizontal
|
||||||
@@ -131,12 +131,11 @@ export function TrendingVideos() {
|
|||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</BlockDrawerGesture>
|
</BlockDrawerGesture>
|
||||||
|
|
||||||
<Prompt.Basic
|
<Prompt.Basic
|
||||||
control={trendingPrompt}
|
control={trendingPrompt}
|
||||||
title={_(msg`Hide trending videos?`)}
|
title={l`Hide trending videos?`}
|
||||||
description={_(msg`You can update this later from your settings.`)}
|
description={l`You can update this later from your settings.`}
|
||||||
confirmButtonCta={_(msg`Hide`)}
|
confirmButtonCta={l`Hide`}
|
||||||
onConfirm={onConfirmHide}
|
onConfirm={onConfirmHide}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
@@ -186,7 +185,7 @@ function VideoCards({
|
|||||||
|
|
||||||
function ViewMoreCard() {
|
function ViewMoreCard() {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
|
|
||||||
const href = useMemo(() => {
|
const href = useMemo(() => {
|
||||||
const urip = new AtUri(VIDEO_FEED_URI)
|
const urip = new AtUri(VIDEO_FEED_URI)
|
||||||
@@ -197,16 +196,16 @@ function ViewMoreCard() {
|
|||||||
<View style={[{width: CARD_WIDTH * 2}]}>
|
<View style={[{width: CARD_WIDTH * 2}]}>
|
||||||
<Link
|
<Link
|
||||||
to={href}
|
to={href}
|
||||||
label={_(msg`View more`)}
|
label={l`View more`}
|
||||||
style={[
|
style={[
|
||||||
a.justify_center,
|
a.justify_center,
|
||||||
a.align_center,
|
a.align_center,
|
||||||
a.flex_1,
|
a.flex_1,
|
||||||
a.rounded_lg,
|
a.rounded_xl,
|
||||||
a.border,
|
a.border,
|
||||||
t.atoms.border_contrast_low,
|
t.atoms.border_contrast_low,
|
||||||
t.atoms.bg,
|
t.atoms.bg,
|
||||||
t.atoms.shadow_sm,
|
t.atoms.shadow_md,
|
||||||
]}>
|
]}>
|
||||||
{({pressed}) => (
|
{({pressed}) => (
|
||||||
<View
|
<View
|
||||||
@@ -225,7 +224,7 @@ function ViewMoreCard() {
|
|||||||
color="primary"
|
color="primary"
|
||||||
size="small"
|
size="small"
|
||||||
shape="round"
|
shape="round"
|
||||||
label={_(msg`View more trending videos`)}>
|
label={l`View more trending videos`}>
|
||||||
<ButtonIcon icon={ChevronRight} />
|
<ButtonIcon icon={ChevronRight} />
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {atoms as a, native, useTheme, type ViewStyleProp} from '#/alf'
|
|||||||
import {Button, ButtonIcon} from '#/components/Button'
|
import {Button, ButtonIcon} from '#/components/Button'
|
||||||
import * as FeedCard from '#/components/FeedCard'
|
import * as FeedCard from '#/components/FeedCard'
|
||||||
import {sizes as iconSizes} from '#/components/icons/common'
|
import {sizes as iconSizes} from '#/components/icons/common'
|
||||||
|
import {DotGrid3x1_Stroke2_Corner0_Rounded as EllipsisIcon} from '#/components/icons/DotGrid'
|
||||||
import {MagnifyingGlass_Stroke2_Corner0_Rounded as SearchIcon} from '#/components/icons/MagnifyingGlass'
|
import {MagnifyingGlass_Stroke2_Corner0_Rounded as SearchIcon} from '#/components/icons/MagnifyingGlass'
|
||||||
import {Link} from '#/components/Link'
|
import {Link} from '#/components/Link'
|
||||||
import {Text, type TextProps} from '#/components/Typography'
|
import {Text, type TextProps} from '#/components/Typography'
|
||||||
@@ -151,6 +152,32 @@ export function SearchButton({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function EllipsisButton({
|
||||||
|
label,
|
||||||
|
onPress,
|
||||||
|
}: {
|
||||||
|
label: string
|
||||||
|
onPress?: () => void
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
label={label}
|
||||||
|
size="small"
|
||||||
|
variant="ghost"
|
||||||
|
color="secondary"
|
||||||
|
shape="round"
|
||||||
|
PressableComponent={native(PressableScale)}
|
||||||
|
onPress={onPress}
|
||||||
|
style={[
|
||||||
|
{
|
||||||
|
right: -4,
|
||||||
|
},
|
||||||
|
]}>
|
||||||
|
<ButtonIcon icon={EllipsisIcon} size="md" />
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export function PinButton({feed}: {feed: AppBskyFeedDefs.GeneratorView}) {
|
export function PinButton({feed}: {feed: AppBskyFeedDefs.GeneratorView}) {
|
||||||
return (
|
return (
|
||||||
<View style={[a.z_20, {marginRight: -6}]}>
|
<View style={[a.z_20, {marginRight: -6}]}>
|
||||||
|
|||||||
@@ -9,7 +9,10 @@ import {
|
|||||||
import {Plural, Trans, useLingui} from '@lingui/react/macro'
|
import {Plural, Trans, useLingui} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||||
import {useTrendingSettings} from '#/state/preferences/trending'
|
import {
|
||||||
|
useTrendingSettings,
|
||||||
|
useTrendingSettingsApi,
|
||||||
|
} from '#/state/preferences/trending'
|
||||||
import {useGetTrendsQuery} from '#/state/queries/trending/useGetTrendsQuery'
|
import {useGetTrendsQuery} from '#/state/queries/trending/useGetTrendsQuery'
|
||||||
import {useTrendingConfig} from '#/state/service-config'
|
import {useTrendingConfig} from '#/state/service-config'
|
||||||
import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
|
import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
|
||||||
@@ -18,6 +21,7 @@ import {atoms as a, useGutters, useTheme, type ViewStyleProp} from '#/alf'
|
|||||||
import {AvatarStack} from '#/components/AvatarStack'
|
import {AvatarStack} from '#/components/AvatarStack'
|
||||||
import {Trending3_Stroke2_Corner1_Rounded as TrendingIcon} from '#/components/icons/Trending'
|
import {Trending3_Stroke2_Corner1_Rounded as TrendingIcon} from '#/components/icons/Trending'
|
||||||
import {Link} from '#/components/Link'
|
import {Link} from '#/components/Link'
|
||||||
|
import * as Prompt from '#/components/Prompt'
|
||||||
import {RichText} from '#/components/RichText'
|
import {RichText} from '#/components/RichText'
|
||||||
import {SubtleHover} from '#/components/SubtleHover'
|
import {SubtleHover} from '#/components/SubtleHover'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
@@ -36,6 +40,9 @@ export function ExploreTrendingTopics() {
|
|||||||
|
|
||||||
function Inner() {
|
function Inner() {
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
|
const {t: l} = useLingui()
|
||||||
|
const trendingPrompt = Prompt.usePromptControl()
|
||||||
|
const {setTrendingDisabled} = useTrendingSettingsApi()
|
||||||
const {data: trending, error, isLoading, isRefetching} = useGetTrendsQuery()
|
const {data: trending, error, isLoading, isRefetching} = useGetTrendsQuery()
|
||||||
const noTopics = !isLoading && !error && !trending?.trends?.length
|
const noTopics = !isLoading && !error && !trending?.trends?.length
|
||||||
const showLoading = isLoading || isRefetching
|
const showLoading = isLoading || isRefetching
|
||||||
@@ -43,31 +50,48 @@ function Inner() {
|
|||||||
if (!showLoading && (error || !trending?.trends || noTopics)) return null
|
if (!showLoading && (error || !trending?.trends || noTopics)) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[a.pb_md]}>
|
<>
|
||||||
<ModuleHeader.Container bottomBorder>
|
<View style={[a.pb_md]}>
|
||||||
<ModuleHeader.Icon icon={TrendingIcon} size="md" />
|
<ModuleHeader.Container bottomBorder>
|
||||||
<ModuleHeader.TitleText>
|
<ModuleHeader.Icon icon={TrendingIcon} size="md" />
|
||||||
<Trans>Trending</Trans>
|
<ModuleHeader.TitleText>
|
||||||
</ModuleHeader.TitleText>
|
<Trans>Trending</Trans>
|
||||||
</ModuleHeader.Container>
|
</ModuleHeader.TitleText>
|
||||||
{showLoading
|
<ModuleHeader.EllipsisButton
|
||||||
? Array.from({length: TOPIC_COUNT}).map((__, i) => (
|
label={l`Trending options`}
|
||||||
<TrendingTopicRowSkeleton key={i} />
|
onPress={() => trendingPrompt.open()}
|
||||||
))
|
/>
|
||||||
: trending?.trends.map((trend, index) => (
|
</ModuleHeader.Container>
|
||||||
<TrendRow
|
{showLoading
|
||||||
key={trend.link}
|
? Array.from({length: TOPIC_COUNT}).map((__, i) => (
|
||||||
trend={trend}
|
<TrendingTopicRowSkeleton key={i} />
|
||||||
rank={index + 1}
|
))
|
||||||
onPress={() => {
|
: trending?.trends.map((trend, index) => (
|
||||||
ax.metric('trendingTopic:click', {
|
<TrendRow
|
||||||
context: 'explore',
|
key={trend.link}
|
||||||
recId: trending.recId,
|
trend={trend}
|
||||||
})
|
rank={index + 1}
|
||||||
}}
|
onPress={() => {
|
||||||
/>
|
ax.metric('trendingTopic:click', {
|
||||||
))}
|
context: 'explore',
|
||||||
</View>
|
recId: trending.recId,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<Prompt.Basic
|
||||||
|
control={trendingPrompt}
|
||||||
|
title={l`Hide trending topics?`}
|
||||||
|
description={l`You can update this later from your settings.`}
|
||||||
|
confirmButtonCta={l`Hide`}
|
||||||
|
onConfirm={() => {
|
||||||
|
ax.metric('trendingTopics:hide', {context: 'explore:trending'})
|
||||||
|
setTrendingDisabled(true)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +136,7 @@ export function TrendRow({
|
|||||||
<View style={[gutters, a.w_full, a.flex_row, a.py_md, a.gap_sm]}>
|
<View style={[gutters, a.w_full, a.flex_row, a.py_md, a.gap_sm]}>
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
a.text_sm,
|
a.text_md,
|
||||||
a.font_medium,
|
a.font_medium,
|
||||||
|
|
||||||
t.atoms.text_contrast_low,
|
t.atoms.text_contrast_low,
|
||||||
@@ -126,7 +150,7 @@ export function TrendRow({
|
|||||||
</Text>
|
</Text>
|
||||||
<View style={[a.flex_1, a.gap_2xs]}>
|
<View style={[a.flex_1, a.gap_2xs]}>
|
||||||
<Text
|
<Text
|
||||||
style={[a.text_sm, a.font_semi_bold, a.leading_snug]}
|
style={[a.text_md, a.font_semi_bold, a.leading_snug]}
|
||||||
numberOfLines={1}>
|
numberOfLines={1}>
|
||||||
{trend.displayName}
|
{trend.displayName}
|
||||||
</Text>
|
</Text>
|
||||||
|
|||||||
@@ -211,6 +211,10 @@ export type PostFeedRef = {
|
|||||||
// const REFRESH_AFTER = STALE.HOURS.ONE
|
// const REFRESH_AFTER = STALE.HOURS.ONE
|
||||||
const CHECK_LATEST_AFTER = STALE.SECONDS.THIRTY
|
const CHECK_LATEST_AFTER = STALE.SECONDS.THIRTY
|
||||||
|
|
||||||
|
const TRENDING_TOPICS_INDEX = 5
|
||||||
|
const TRENDING_VIDEO_INDEX = 30
|
||||||
|
const SUGGESTED_FOR_YOU_INDEX = 15
|
||||||
|
|
||||||
let PostFeed = ({
|
let PostFeed = ({
|
||||||
feed,
|
feed,
|
||||||
description,
|
description,
|
||||||
@@ -570,19 +574,19 @@ let PostFeed = ({
|
|||||||
key: 'composerPrompt-' + sliceIndex,
|
key: 'composerPrompt-' + sliceIndex,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else if (sliceIndex === 1) {
|
} else if (sliceIndex === TRENDING_TOPICS_INDEX) {
|
||||||
arr.push({
|
arr.push({
|
||||||
type: 'interstitialFeedTrendingTopics',
|
type: 'interstitialFeedTrendingTopics',
|
||||||
key: 'interstitialFeedTrendingTopics-' + sliceIndex,
|
key: 'interstitialFeedTrendingTopics-' + sliceIndex,
|
||||||
})
|
})
|
||||||
} else if (sliceIndex === 15) {
|
} else if (sliceIndex === TRENDING_VIDEO_INDEX) {
|
||||||
if (areVideoFeedsEnabled && !trendingVideoDisabled) {
|
if (areVideoFeedsEnabled && !trendingVideoDisabled) {
|
||||||
arr.push({
|
arr.push({
|
||||||
type: 'interstitialTrendingVideos',
|
type: 'interstitialTrendingVideos',
|
||||||
key: 'interstitial-' + sliceIndex + '-' + lastFetchedAt,
|
key: 'interstitial-' + sliceIndex + '-' + lastFetchedAt,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else if (sliceIndex === 30) {
|
} else if (sliceIndex === SUGGESTED_FOR_YOU_INDEX) {
|
||||||
arr.push({
|
arr.push({
|
||||||
type: 'interstitialFollows',
|
type: 'interstitialFollows',
|
||||||
key: 'interstitial-' + sliceIndex + '-' + lastFetchedAt,
|
key: 'interstitial-' + sliceIndex + '-' + lastFetchedAt,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {useGetTrendsQuery} from '#/state/queries/trending/useGetTrendsQuery'
|
|||||||
import {useTrendingConfig} from '#/state/service-config'
|
import {useTrendingConfig} from '#/state/service-config'
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
import {Button, ButtonIcon} from '#/components/Button'
|
import {Button, ButtonIcon} from '#/components/Button'
|
||||||
import {DotGrid3x1_Stroke2_Corner0_Rounded as Ellipsis} from '#/components/icons/DotGrid'
|
import {DotGrid3x1_Stroke2_Corner0_Rounded as EllipsisIcon} from '#/components/icons/DotGrid'
|
||||||
import {Trending3_Stroke2_Corner1_Rounded as TrendingIcon} from '#/components/icons/Trending'
|
import {Trending3_Stroke2_Corner1_Rounded as TrendingIcon} from '#/components/icons/Trending'
|
||||||
import * as Prompt from '#/components/Prompt'
|
import * as Prompt from '#/components/Prompt'
|
||||||
import {TrendingTopicLink} from '#/components/TrendingTopics'
|
import {TrendingTopicLink} from '#/components/TrendingTopics'
|
||||||
@@ -50,7 +50,7 @@ function Inner() {
|
|||||||
style={[a.p_lg, a.rounded_md, a.border, t.atoms.border_contrast_low]}>
|
style={[a.p_lg, a.rounded_md, a.border, t.atoms.border_contrast_low]}>
|
||||||
<View style={[a.flex_row, a.align_center, a.gap_xs, a.pb_md]}>
|
<View style={[a.flex_row, a.align_center, a.gap_xs, a.pb_md]}>
|
||||||
<TrendingIcon width={16} height={16} fill={t.atoms.text.color} />
|
<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]}>
|
<Text style={[a.flex_1, a.text_md, a.font_semi_bold]}>
|
||||||
<Trans>Trending</Trans>
|
<Trans>Trending</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
<Button
|
<Button
|
||||||
@@ -60,8 +60,8 @@ function Inner() {
|
|||||||
shape="round"
|
shape="round"
|
||||||
label={l`Trending options`}
|
label={l`Trending options`}
|
||||||
onPress={() => trendingPrompt.open()}
|
onPress={() => trendingPrompt.open()}
|
||||||
style={[a.bg_transparent, {marginTop: -6, marginRight: -6}]}>
|
style={[a.bg_transparent]}>
|
||||||
<ButtonIcon icon={Ellipsis} size="xs" />
|
<ButtonIcon icon={EllipsisIcon} size="xs" />
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user