Add Discover banner/trending handling, device storage

This commit is contained in:
Eric Bailey
2026-01-13 16:32:13 -06:00
parent e23a92126a
commit 9c1eb63620
6 changed files with 114 additions and 13 deletions
+1 -1
View File
@@ -41,7 +41,7 @@ export function Inner() {
}, [setTrendingDisabled])
return error || noTopics ? null : (
<View style={[t.atoms.border_contrast_low, a.border_t]}>
<View style={[t.atoms.border_contrast_low, a.border_t, a.border_b]}>
<BlockDrawerGesture>
<ScrollView
horizontal
@@ -0,0 +1,98 @@
import {View} from 'react-native'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useTrendingSettings} from '#/state/preferences/trending'
import {atoms as a, useLayoutBreakpoints, useTheme} from '#/alf'
import {Button} from '#/components/Button'
import {TimesLarge_Stroke2_Corner0_Rounded as CloseIcon} from '#/components/icons/Times'
import {TrendingInterstitial} from '#/components/interstitials/Trending'
import * as Prompt from '#/components/Prompt'
import {LiveEventFeedCardWide} from '#/features/liveEvents/components/LiveEventFeedCardWide'
import {useLiveEvents} from '#/features/liveEvents/context'
import {device, useStorage} from '#/storage'
export function DiscoverFeedLiveEventFeedsAndTrendingBanner() {
const t = useTheme()
const {_} = useLingui()
const events = useLiveEvents()
const {rightNavVisible} = useLayoutBreakpoints()
const {trendingDisabled} = useTrendingSettings()
const promptControl = Prompt.usePromptControl()
const [bannerDisabled, setBannerDisabled] = useStorage(device, [
'liveEventFeedsBannerDisabled',
])
const feed = events.feeds.at(0)
if (!feed || bannerDisabled) {
if (!rightNavVisible && !trendingDisabled) {
// only show trending on mobile when live event banner is not shown
return <TrendingInterstitial />
} else {
// no feed, no trending
return null
}
}
// On desktop, we show in the sidebar
if (rightNavVisible) return null
const image = feed.images.wide
return (
<>
<View style={[a.px_lg, a.pt_md, a.pb_xs]}>
<View>
<LiveEventFeedCardWide feed={feed} />
<Button
label={_(msg`Configure live event banner`)}
size="tiny"
shape="round"
style={[a.absolute, a.z_10, {top: 6, right: 6}]}
onPress={() => {
promptControl.open()
}}>
{({hovered, pressed}) => (
<>
<View
style={[
a.absolute,
a.inset_0,
a.rounded_full,
{
backgroundColor: feed.images.wide.overlayColor,
opacity: hovered || pressed ? 0.8 : 0.6,
},
]}
/>
<CloseIcon
size="xs"
fill={
image.textColor === 'light'
? t.palette.white
: t.palette.black
}
style={[a.z_20]}
/>
</>
)}
</Button>
</View>
</View>
<Prompt.Basic
control={promptControl}
title={_(msg`Disable the live event banner?`)}
description={_(
msg`Live events appear occasionally when something exciting is happening that we think you might like. You can always re-enable this banner from your Content & Media settings page.`,
)}
confirmButtonCta={_(msg`Disable banner`)}
onConfirm={() => {
setBannerDisabled(true)
}}
/>
</>
)
}
+1
View File
@@ -3,6 +3,7 @@ export type LiveEventFeedImageLayout = 'wide' // maybe more in the future
export type LiveEventFeedImage = {
alt: string
overlayColor: string
textColor: string
url: string
blurhash: string
}
+2
View File
@@ -50,6 +50,8 @@ export type Device = {
activitySubscriptionsNudged?: boolean
threadgateNudged?: boolean
liveEventFeedsBannerDisabled?: boolean
/**
* Policy update overlays. New IDs are required for each new announcement.
*/
-2
View File
@@ -148,8 +148,6 @@ export function ComposerPrompt() {
a.relative,
a.flex_row,
a.align_start,
a.border_t,
t.atoms.border_contrast_low,
{
paddingLeft: 18,
paddingRight: 15,
+12 -10
View File
@@ -70,6 +70,7 @@ import {
} from '#/components/feeds/PostFeedVideoGridRow'
import {TrendingInterstitial} from '#/components/interstitials/Trending'
import {TrendingVideos as TrendingVideosInterstitial} from '#/components/interstitials/TrendingVideos'
import {DiscoverFeedLiveEventFeedsAndTrendingBanner} from '#/features/liveEvents/components/DiscoverFeedLiveEventFeedsAndTrendingBanner'
import {ComposerPrompt} from '../feeds/ComposerPrompt'
import {DiscoverFallbackHeader} from './DiscoverFallbackHeader'
import {FeedShutdownMsg} from './FeedShutdownMsg'
@@ -155,6 +156,10 @@ type FeedRow =
type: 'composerPrompt'
key: string
}
| {
type: 'liveEventFeedsAndTrendingBanner'
key: string
}
export function getItemsForFeedback(feedRow: FeedRow): {
item: FeedPostSliceItem
@@ -360,7 +365,7 @@ let PostFeed = ({
const showProgressIntersitial =
(followProgressGuide || followAndLikeProgressGuide) && !rightNavVisible
const {trendingDisabled, trendingVideoDisabled} = useTrendingSettings()
const {trendingVideoDisabled} = useTrendingSettings()
const ageAssuranceBannerState = useAgeAssuranceBannerState()
const selectedFeed = useSelectedFeed()
@@ -510,13 +515,10 @@ let PostFeed = ({
})
}
}
if (!rightNavVisible && !trendingDisabled) {
arr.push({
type: 'interstitialTrending',
key:
'interstitial2-' + sliceIndex + '-' + lastFetchedAt,
})
}
arr.push({
type: 'liveEventFeedsAndTrendingBanner',
key: 'liveEventFeedsAndTrendingBanner-' + sliceIndex,
})
// Show composer prompt for Discover and Following feeds
if (
hasSession &&
@@ -672,9 +674,7 @@ let PostFeed = ({
feedTab,
hasSession,
showProgressIntersitial,
trendingDisabled,
trendingVideoDisabled,
rightNavVisible,
gtMobile,
isVideoFeed,
areVideoFeedsEnabled,
@@ -773,6 +773,8 @@ let PostFeed = ({
return <AgeAssuranceDismissibleFeedBanner />
} else if (row.type === 'interstitialTrending') {
return <TrendingInterstitial />
} else if (row.type === 'liveEventFeedsAndTrendingBanner') {
return <DiscoverFeedLiveEventFeedsAndTrendingBanner />
} else if (row.type === 'composerPrompt') {
return <ComposerPrompt />
} else if (row.type === 'interstitialTrendingVideos') {