Handle live event in feed card

This commit is contained in:
Eric Bailey
2026-01-14 11:27:59 -06:00
parent bb4dbbfec2
commit be0e78b45a
4 changed files with 60 additions and 6 deletions
+35 -3
View File
@@ -1,4 +1,4 @@
import React from 'react' import React, {useMemo} from 'react'
import {type GestureResponderEvent, View} from 'react-native' import {type GestureResponderEvent, View} from 'react-native'
import { import {
type AppBskyFeedDefs, type AppBskyFeedDefs,
@@ -21,19 +21,21 @@ import {
import {useSession} from '#/state/session' import {useSession} from '#/state/session'
import * as Toast from '#/view/com/util/Toast' import * as Toast from '#/view/com/util/Toast'
import {UserAvatar} from '#/view/com/util/UserAvatar' import {UserAvatar} from '#/view/com/util/UserAvatar'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, select, useTheme} from '#/alf'
import { import {
Button, Button,
ButtonIcon, ButtonIcon,
type ButtonProps, type ButtonProps,
ButtonText, ButtonText,
} from '#/components/Button' } from '#/components/Button'
import {Live_Stroke2_Corner0_Rounded as LiveIcon} from '#/components/icons/Live'
import {Pin_Stroke2_Corner0_Rounded as PinIcon} from '#/components/icons/Pin' import {Pin_Stroke2_Corner0_Rounded as PinIcon} from '#/components/icons/Pin'
import {Link as InternalLink, type LinkProps} from '#/components/Link' import {Link as InternalLink, type LinkProps} from '#/components/Link'
import {Loader} from '#/components/Loader' import {Loader} from '#/components/Loader'
import * as Prompt from '#/components/Prompt' import * as Prompt from '#/components/Prompt'
import {RichText, type RichTextProps} from '#/components/RichText' import {RichText, type RichTextProps} from '#/components/RichText'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {useActiveLiveEventFeedUris} from '#/features/liveEvents/context'
import type * as bsky from '#/types/bsky' import type * as bsky from '#/types/bsky'
import {Trash_Stroke2_Corner0_Rounded as TrashIcon} from './icons/Trash' import {Trash_Stroke2_Corner0_Rounded as TrashIcon} from './icons/Trash'
@@ -49,7 +51,11 @@ export function Default(props: Props) {
<Outer> <Outer>
<Header> <Header>
<Avatar src={view.avatar} /> <Avatar src={view.avatar} />
<TitleAndByline title={view.displayName} creator={view.creator} /> <TitleAndByline
title={view.displayName}
creator={view.creator}
uri={view.uri}
/>
<SaveButton view={view} pin /> <SaveButton view={view} pin />
</Header> </Header>
<Description description={view.description} /> <Description description={view.description} />
@@ -118,14 +124,40 @@ export function AvatarPlaceholder({size = 40}: Omit<AvatarProps, 'src'>) {
export function TitleAndByline({ export function TitleAndByline({
title, title,
creator, creator,
uri,
}: { }: {
title: string title: string
creator?: bsky.profile.AnyProfileView creator?: bsky.profile.AnyProfileView
uri?: string
}) { }) {
const t = useTheme() const t = useTheme()
const activeLiveEvents = useActiveLiveEventFeedUris()
const liveColor = useMemo(
() =>
select(t.name, {
dark: t.palette.negative_600,
dim: t.palette.negative_600,
light: t.palette.negative_500,
}),
[t],
)
return ( return (
<View style={[a.flex_1]}> <View style={[a.flex_1]}>
{uri && activeLiveEvents.has(uri) && (
<View style={[a.flex_row, a.align_center, a.gap_2xs]}>
<LiveIcon size="xs" fill={liveColor} />
<Text
style={[
a.text_2xs,
a.font_medium,
a.leading_snug,
{color: liveColor},
]}>
<Trans>Happening now</Trans>
</Text>
</View>
)}
<Text <Text
emoji emoji
style={[a.text_md, a.font_semi_bold, a.leading_snug]} style={[a.text_md, a.font_semi_bold, a.leading_snug]}
+1
View File
@@ -842,6 +842,7 @@ export function SuggestedFeeds() {
<FeedCard.TitleAndByline <FeedCard.TitleAndByline
title={feed.displayName} title={feed.displayName}
creator={feed.creator} creator={feed.creator}
uri={feed.uri}
/> />
</FeedCard.Header> </FeedCard.Header>
<FeedCard.Description <FeedCard.Description
+3 -3
View File
@@ -17,16 +17,16 @@ export function FeedEmbed({
return ( return (
<FeedCard.Link <FeedCard.Link
view={embed.view} view={embed.view}
style={[a.border, t.atoms.border_contrast_low, a.p_md, a.rounded_sm]}> style={[a.border, t.atoms.border_contrast_low, a.p_sm, a.rounded_md]}>
<FeedCard.Outer> <FeedCard.Outer>
<FeedCard.Header> <FeedCard.Header>
<FeedCard.Avatar src={embed.view.avatar} /> <FeedCard.Avatar src={embed.view.avatar} size={48} />
<FeedCard.TitleAndByline <FeedCard.TitleAndByline
title={embed.view.displayName} title={embed.view.displayName}
creator={embed.view.creator} creator={embed.view.creator}
uri={embed.view.uri}
/> />
</FeedCard.Header> </FeedCard.Header>
<FeedCard.Likes count={embed.view.likeCount || 0} />
</FeedCard.Outer> </FeedCard.Outer>
</FeedCard.Link> </FeedCard.Link>
) )
+21
View File
@@ -2,6 +2,11 @@ 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 {useIsBskyTeam} from '#/lib/hooks/useIsBskyTeam'
import {
convertBskyAppUrlIfNeeded,
isBskyCustomFeedUrl,
makeRecordUri,
} from '#/lib/strings/url-helpers'
import {IS_DEV, LIVE_EVENTS_URL} from '#/env' import {IS_DEV, LIVE_EVENTS_URL} from '#/env'
import {useLiveEventPreferences} from '#/features/liveEvents/preferences' import {useLiveEventPreferences} from '#/features/liveEvents/preferences'
import {type LiveEventsWorkerResponse} from '#/features/liveEvents/types' import {type LiveEventsWorkerResponse} from '#/features/liveEvents/types'
@@ -88,3 +93,19 @@ export function useUserPreferencedLiveEvents() {
}), }),
} }
} }
export function useActiveLiveEventFeedUris() {
const {feeds} = useLiveEvents()
return new Set(
feeds
// insurance
.filter(f => isBskyCustomFeedUrl(f.url))
.map(f => {
const uri = convertBskyAppUrlIfNeeded(f.url)
const [_0, did, _1, rkey] = uri.split('/').filter(Boolean)
const urip = makeRecordUri(did, 'app.bsky.feed.generator', rkey)
return urip.toString()
}),
)
}