Add live event feed card to Explore

This commit is contained in:
Eric Bailey
2026-01-13 15:35:03 -06:00
parent fd29569ee7
commit e23a92126a
3 changed files with 140 additions and 0 deletions
@@ -0,0 +1,17 @@
import {View} from 'react-native'
import {atoms as a, useTheme} from '#/alf'
import {LiveEventFeedCardWide} from '#/features/liveEvents/components/LiveEventFeedCardWide'
import {useLiveEvents} from '#/features/liveEvents/context'
export function ExploreScreenLiveEventFeedsBanner() {
const t = useTheme()
const events = useLiveEvents()
const feed = events.feeds.at(0)
if (!feed) return null
return (
<View style={[a.p_lg, a.border_b, t.atoms.border_contrast_low]}>
<LiveEventFeedCardWide feed={feed} />
</View>
)
}
@@ -0,0 +1,113 @@
import {useMemo} from 'react'
import {View} from 'react-native'
import {Image} from 'expo-image'
import {LinearGradient} from 'expo-linear-gradient'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {isBskyCustomFeedUrl} from '#/lib/strings/url-helpers'
import {atoms as a, useBreakpoints, utils} from '#/alf'
import {Link} from '#/components/Link'
import {Text} from '#/components/Typography'
import {type LiveEventFeed} from '#/features/liveEvents/types'
const roundedStyles = [a.rounded_lg, a.curve_continuous]
export function LiveEventFeedCardWide({feed}: {feed: LiveEventFeed}) {
const {_} = useLingui()
const {gtPhone} = useBreakpoints()
const image = feed.images.wide
const url = useMemo(() => {
// Validated in multiple places on the backend
if (isBskyCustomFeedUrl(feed.url)) {
return new URL(feed.url).pathname
}
return '/'
}, [feed.url])
return (
<Link
to={url}
label={_(msg`Live event happening now: ${feed.title}`)}
style={[a.w_full]}>
{({hovered, pressed}) => (
<View style={[roundedStyles, a.shadow_md, a.w_full]}>
<View
style={[
a.align_start,
roundedStyles,
a.overflow_hidden,
{
aspectRatio: gtPhone ? 576 / 144 : 369 / 100,
},
]}>
<Image
accessibilityIgnoresInvertColors
source={{
uri: image.url,
blurhash: image.blurhash,
}}
style={[a.absolute, a.inset_0]}
contentFit="cover"
/>
<LinearGradient
colors={[image.overlayColor, utils.alpha(image.overlayColor, 0)]}
locations={[0, 1]}
start={{x: 0, y: 0}}
end={{x: 1, y: 0}}
style={[
a.absolute,
a.inset_0,
a.transition_opacity,
{
transitionDuration: '200ms',
opacity: hovered || pressed ? 0.6 : 0,
},
]}
/>
<View style={[a.flex_1, a.justify_end]}>
<LinearGradient
colors={[
image.overlayColor,
utils.alpha(image.overlayColor, 0),
]}
locations={[0, 1]}
start={{x: 0, y: 0}}
end={{x: 1, y: 0}}
style={[a.absolute, a.inset_0]}
/>
<View
style={[
a.z_10,
gtPhone ? [a.pl_xl, a.pb_lg] : [a.pl_lg, a.pb_md],
{paddingRight: 64},
]}>
<Text
style={[
a.leading_snug,
gtPhone ? a.text_xs : a.text_2xs,
{color: 'white', opacity: 0.8},
]}>
<Trans>Happening now</Trans>
</Text>
<Text
style={[
a.leading_snug,
a.font_bold,
gtPhone ? a.text_3xl : a.text_lg,
{color: 'white'},
]}>
{feed.title}
</Text>
</View>
</View>
</View>
</View>
)}
</Link>
)
}
+10
View File
@@ -68,6 +68,7 @@ import {Loader} from '#/components/Loader'
import * as ProfileCard from '#/components/ProfileCard'
import {SubtleHover} from '#/components/SubtleHover'
import {Text} from '#/components/Typography'
import {ExploreScreenLiveEventFeedsBanner} from '#/features/liveEvents/components/ExploreScreenLiveEventFeedsBanner'
import * as ModuleHeader from './components/ModuleHeader'
import {
SuggestedAccountsTabBar,
@@ -201,6 +202,10 @@ type ExploreScreenItems =
type: 'interests-card'
key: 'interests-card'
}
| {
type: 'liveEventFeedsBanner'
key: string
}
export function Explore({
focusSearchInput,
@@ -684,6 +689,8 @@ export function Explore({
i.push(topBorder)
i.push(...interestsNuxModule)
i.push({type: 'liveEventFeedsBanner', key: 'liveEventFeedsBanner'})
if (useFullExperience) {
i.push(trendingTopicsModule)
i.push(...suggestedFeedsModule)
@@ -998,6 +1005,9 @@ export function Explore({
case 'interests-card': {
return <ExploreInterestsCard />
}
case 'liveEventFeedsBanner': {
return <ExploreScreenLiveEventFeedsBanner />
}
}
},
[