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