Handle previews

This commit is contained in:
Eric Bailey
2026-01-14 10:32:22 -06:00
parent 76af7566b6
commit 7b6698de8c
7 changed files with 156 additions and 112 deletions
+46 -46
View File
@@ -146,49 +146,51 @@ function InnerApp() {
<QueryProvider currentDid={currentAccount?.did}>
<PolicyUpdateOverlayProvider>
<StatsigProvider>
<AgeAssuranceV2Provider>
<ComposerProvider>
<MessagesProvider>
{/* LabelDefsProvider MUST come before ModerationOptsProvider */}
<LabelDefsProvider>
<ModerationOptsProvider>
<LoggedOutViewProvider>
<SelectedFeedProvider>
<HiddenRepliesProvider>
<HomeBadgeProvider>
<UnreadNotifsProvider>
<BackgroundNotificationPreferencesProvider>
<MutedThreadsProvider>
<ProgressGuideProvider>
<ServiceAccountManager>
<EmailVerificationProvider>
<HideBottomBarBorderProvider>
<GestureHandlerRootView
style={s.h100pct}>
<GlobalGestureEventsProvider>
<IntentDialogProvider>
<TestCtrls />
<Shell />
<ToastOutlet />
</IntentDialogProvider>
</GlobalGestureEventsProvider>
</GestureHandlerRootView>
</HideBottomBarBorderProvider>
</EmailVerificationProvider>
</ServiceAccountManager>
</ProgressGuideProvider>
</MutedThreadsProvider>
</BackgroundNotificationPreferencesProvider>
</UnreadNotifsProvider>
</HomeBadgeProvider>
</HiddenRepliesProvider>
</SelectedFeedProvider>
</LoggedOutViewProvider>
</ModerationOptsProvider>
</LabelDefsProvider>
</MessagesProvider>
</ComposerProvider>
</AgeAssuranceV2Provider>
<LiveEventsProvider>
<AgeAssuranceV2Provider>
<ComposerProvider>
<MessagesProvider>
{/* LabelDefsProvider MUST come before ModerationOptsProvider */}
<LabelDefsProvider>
<ModerationOptsProvider>
<LoggedOutViewProvider>
<SelectedFeedProvider>
<HiddenRepliesProvider>
<HomeBadgeProvider>
<UnreadNotifsProvider>
<BackgroundNotificationPreferencesProvider>
<MutedThreadsProvider>
<ProgressGuideProvider>
<ServiceAccountManager>
<EmailVerificationProvider>
<HideBottomBarBorderProvider>
<GestureHandlerRootView
style={s.h100pct}>
<GlobalGestureEventsProvider>
<IntentDialogProvider>
<TestCtrls />
<Shell />
<ToastOutlet />
</IntentDialogProvider>
</GlobalGestureEventsProvider>
</GestureHandlerRootView>
</HideBottomBarBorderProvider>
</EmailVerificationProvider>
</ServiceAccountManager>
</ProgressGuideProvider>
</MutedThreadsProvider>
</BackgroundNotificationPreferencesProvider>
</UnreadNotifsProvider>
</HomeBadgeProvider>
</HiddenRepliesProvider>
</SelectedFeedProvider>
</LoggedOutViewProvider>
</ModerationOptsProvider>
</LabelDefsProvider>
</MessagesProvider>
</ComposerProvider>
</AgeAssuranceV2Provider>
</LiveEventsProvider>
</StatsigProvider>
</PolicyUpdateOverlayProvider>
</QueryProvider>
@@ -235,9 +237,7 @@ function App() {
<StarterPackProvider>
<SafeAreaProvider
initialMetrics={initialWindowMetrics}>
<LiveEventsProvider>
<InnerApp />
</LiveEventsProvider>
<InnerApp />
</SafeAreaProvider>
</StarterPackProvider>
</BottomSheetProvider>
+42 -42
View File
@@ -122,45 +122,47 @@ function InnerApp() {
<QueryProvider currentDid={currentAccount?.did}>
<PolicyUpdateOverlayProvider>
<StatsigProvider>
<AgeAssuranceV2Provider>
<ComposerProvider>
<MessagesProvider>
{/* LabelDefsProvider MUST come before ModerationOptsProvider */}
<LabelDefsProvider>
<ModerationOptsProvider>
<LoggedOutViewProvider>
<SelectedFeedProvider>
<HiddenRepliesProvider>
<HomeBadgeProvider>
<UnreadNotifsProvider>
<BackgroundNotificationPreferencesProvider>
<MutedThreadsProvider>
<SafeAreaProvider>
<ProgressGuideProvider>
<ServiceConfigProvider>
<EmailVerificationProvider>
<HideBottomBarBorderProvider>
<IntentDialogProvider>
<Shell />
<ToastOutlet />
</IntentDialogProvider>
</HideBottomBarBorderProvider>
</EmailVerificationProvider>
</ServiceConfigProvider>
</ProgressGuideProvider>
</SafeAreaProvider>
</MutedThreadsProvider>
</BackgroundNotificationPreferencesProvider>
</UnreadNotifsProvider>
</HomeBadgeProvider>
</HiddenRepliesProvider>
</SelectedFeedProvider>
</LoggedOutViewProvider>
</ModerationOptsProvider>
</LabelDefsProvider>
</MessagesProvider>
</ComposerProvider>
</AgeAssuranceV2Provider>
<LiveEventsProvider>
<AgeAssuranceV2Provider>
<ComposerProvider>
<MessagesProvider>
{/* LabelDefsProvider MUST come before ModerationOptsProvider */}
<LabelDefsProvider>
<ModerationOptsProvider>
<LoggedOutViewProvider>
<SelectedFeedProvider>
<HiddenRepliesProvider>
<HomeBadgeProvider>
<UnreadNotifsProvider>
<BackgroundNotificationPreferencesProvider>
<MutedThreadsProvider>
<SafeAreaProvider>
<ProgressGuideProvider>
<ServiceConfigProvider>
<EmailVerificationProvider>
<HideBottomBarBorderProvider>
<IntentDialogProvider>
<Shell />
<ToastOutlet />
</IntentDialogProvider>
</HideBottomBarBorderProvider>
</EmailVerificationProvider>
</ServiceConfigProvider>
</ProgressGuideProvider>
</SafeAreaProvider>
</MutedThreadsProvider>
</BackgroundNotificationPreferencesProvider>
</UnreadNotifsProvider>
</HomeBadgeProvider>
</HiddenRepliesProvider>
</SelectedFeedProvider>
</LoggedOutViewProvider>
</ModerationOptsProvider>
</LabelDefsProvider>
</MessagesProvider>
</ComposerProvider>
</AgeAssuranceV2Provider>
</LiveEventsProvider>
</StatsigProvider>
</PolicyUpdateOverlayProvider>
</QueryProvider>
@@ -203,9 +205,7 @@ function App() {
<LightboxStateProvider>
<PortalProvider>
<StarterPackProvider>
<LiveEventsProvider>
<InnerApp />
</LiveEventsProvider>
<InnerApp />
</StarterPackProvider>
</PortalProvider>
</LightboxStateProvider>
@@ -12,26 +12,15 @@ import {
LiveEventFeedOptionsMenu,
useDialogControl,
} from '#/features/liveEvents/components/LiveEventFeedOptionsMenu'
import {useLiveEvents} from '#/features/liveEvents/context'
import {useLiveEventPreferences} from '#/features/liveEvents/preferences'
import {useUserPreferencedLiveEvents} from '#/features/liveEvents/context'
import {type LiveEventFeed} from '#/features/liveEvents/types'
export function DiscoverFeedLiveEventFeedsAndTrendingBanner() {
const t = useTheme()
const {_} = useLingui()
const events = useLiveEvents()
const events = useUserPreferencedLiveEvents()
const {rightNavVisible} = useLayoutBreakpoints()
const {trendingDisabled} = useTrendingSettings()
const optionsMenuControl = useDialogControl()
const {data, isLoading} = useLiveEventPreferences()
// user prefs should be loaded by now, but for TS-sake
if (isLoading) return null
const {hideAllFeeds, hiddenFeedIds} = data
const feed = events.feeds.at(0)
const feedHidden = feed?.id ? hiddenFeedIds.includes(feed?.id || '') : false
if (!feed || hideAllFeeds || feedHidden) {
if (!events.feeds.length) {
if (!rightNavVisible && !trendingDisabled) {
// only show trending on mobile when live event banner is not shown
return <TrendingInterstitial />
@@ -44,7 +33,13 @@ export function DiscoverFeedLiveEventFeedsAndTrendingBanner() {
// On desktop, we show in the sidebar
if (rightNavVisible) return null
const image = feed.images.wide
return events.feeds.map(feed => <Inner feed={feed} key={feed.id} />)
}
function Inner({feed}: {feed: LiveEventFeed}) {
const t = useTheme()
const {_} = useLingui()
const optionsMenuControl = useDialogControl()
return (
<>
@@ -76,7 +71,7 @@ export function DiscoverFeedLiveEventFeedsAndTrendingBanner() {
<EllipsisIcon
size="sm"
fill={
image.textColor === 'light'
feed.images.wide.textColor === 'light'
? t.palette.white
: t.palette.black
}
@@ -28,6 +28,7 @@ export function LiveEventFeedCardWide({
const {gtPhone} = useBreakpoints()
const image = feed.images.wide
const textColor = feed.images.wide.textColor === 'light' ? 'white' : 'black'
const url = useMemo(() => {
// Validated in multiple places on the backend
if (isBskyCustomFeedUrl(feed.url)) {
@@ -106,16 +107,20 @@ export function LiveEventFeedCardWide({
style={[
a.leading_snug,
gtPhone ? a.text_xs : a.text_2xs,
{color: 'white', opacity: 0.8},
{color: textColor, opacity: 0.8},
]}>
<Trans>Happening now</Trans>
{feed.preview ? (
<Trans>Preview</Trans>
) : (
<Trans>Happening now</Trans>
)}
</Text>
<Text
style={[
a.leading_snug,
a.font_bold,
gtPhone ? a.text_3xl : a.text_lg,
{color: 'white'},
{color: textColor},
]}>
{feed.title}
</Text>
+39 -4
View File
@@ -1,12 +1,19 @@
import {createContext, useContext} from 'react'
import {QueryClient, useQuery} from '@tanstack/react-query'
import {useIsBskyTeam} from '#/lib/hooks/useIsBskyTeam'
import {IS_DEV, LIVE_EVENTS_URL} from '#/env'
import {useLiveEventPreferences} from '#/features/liveEvents/preferences'
import {type LiveEventsWorkerResponse} from '#/features/liveEvents/types'
import {useDevMode} from '#/storage/hooks/dev-mode'
const qc = new QueryClient()
const liveEventsQueryKey = ['live-events']
export const DEFAULT_LIVE_EVENTS = {
feeds: [],
}
async function fetchLiveEvents(): Promise<LiveEventsWorkerResponse | null> {
try {
const res = await fetch(`${LIVE_EVENTS_URL}/config`)
@@ -18,17 +25,29 @@ async function fetchLiveEvents(): Promise<LiveEventsWorkerResponse | null> {
}
}
const Context = createContext<LiveEventsWorkerResponse>({
feeds: [],
})
const Context = createContext<LiveEventsWorkerResponse>(DEFAULT_LIVE_EVENTS)
export function Provider({children}: React.PropsWithChildren<{}>) {
const [isDevMode] = useDevMode()
const isBskyTeam = useIsBskyTeam()
const {data} = useQuery(
{
staleTime: IS_DEV ? 5e3 : 1000 * 60,
queryKey: liveEventsQueryKey,
async queryFn() {
return fetchLiveEvents()
const events = await fetchLiveEvents()
if (!events) return null
const feeds = events.feeds.filter(f => {
if (f.preview && !isBskyTeam) {
return false
}
return true
})
return {
...events,
// only one at a time for now, unless bsky team and dev mode
feeds: isBskyTeam && isDevMode ? feeds : feeds.slice(0, 1),
}
},
},
qc,
@@ -53,3 +72,19 @@ export function useLiveEvents() {
}
return ctx
}
export function useUserPreferencedLiveEvents() {
const events = useLiveEvents()
const {data, isLoading} = useLiveEventPreferences()
if (isLoading) return DEFAULT_LIVE_EVENTS
const {hideAllFeeds, hiddenFeedIds} = data
return {
...events,
feeds: hideAllFeeds
? []
: events.feeds.filter(f => {
const hidden = f?.id ? hiddenFeedIds.includes(f?.id || '') : false
return !hidden
}),
}
}
+8
View File
@@ -0,0 +1,8 @@
import {useMemo} from 'react'
import {useGate} from '#/lib/statsig/statsig'
export function useIsBskyTeam() {
const gate = useGate()
return useMemo(() => gate('is_bsky_team_member'), [gate])
}
+1
View File
@@ -7,6 +7,7 @@ export type Gate =
| 'disable_settings_find_contacts'
| 'explore_show_suggested_feeds'
| 'feed_reply_button_open_thread'
| 'is_bsky_team_member' // special, do not remove
| 'old_postonboarding'
| 'onboarding_add_video_feed'
| 'onboarding_suggested_starterpacks'