Filter out posts without videos

This commit is contained in:
Eric Bailey
2025-01-16 13:44:43 -06:00
parent 75abe339a5
commit d7ea079307
3 changed files with 35 additions and 23 deletions
+12 -16
View File
@@ -1,12 +1,11 @@
import {Pressable, View} from 'react-native' import {Pressable, View} from 'react-native'
import {Image} from 'expo-image' import {Image} from 'expo-image'
import {LinearGradient} from 'expo-linear-gradient' import {LinearGradient} from 'expo-linear-gradient'
import {AppBskyEmbedVideo} from '@atproto/api' import {AppBskyEmbedVideo,AppBskyFeedDefs} from '@atproto/api'
import {msg} from '@lingui/macro' import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {sanitizeHandle} from '#/lib/strings/handles' import {sanitizeHandle} from '#/lib/strings/handles'
import {FeedPostSliceItem} from '#/state/queries/post-feed'
import {formatCount} from '#/view/com/util/numeric/format' import {formatCount} from '#/view/com/util/numeric/format'
import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar' import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
@@ -17,20 +16,21 @@ import {Heart2_Stroke2_Corner0_Rounded as Heart} from '#/components/icons/Heart2
import {Repost_Stroke2_Corner2_Rounded as Repost} from '#/components/icons/Repost' import {Repost_Stroke2_Corner2_Rounded as Repost} from '#/components/icons/Repost'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
export function VideoPostCard({post}: {post: FeedPostSliceItem}) { export function VideoPostCard({post}: {post: AppBskyFeedDefs.PostView}) {
const t = useTheme() const t = useTheme()
const {_, i18n} = useLingui() const {_, i18n} = useLingui()
const embed = post.post.embed const embed = post.embed
const { const {
state: hovered, state: hovered,
onIn: onHoverIn, onIn: onHoverIn,
onOut: onHoverOut, onOut: onHoverOut,
} = useInteractionState() } = useInteractionState()
if (!AppBskyEmbedVideo.isView(embed)) { /**
// TODO unavailable? * Filtering should be done at a higher level, such as `PostFeed` or
return null * `PostFeedVideoGridRow`, but we need to protect here as well.
} */
if (!AppBskyEmbedVideo.isView(embed)) return null
const {thumbnail} = embed const {thumbnail} = embed
const black = select(t.name, { const black = select(t.name, {
@@ -95,13 +95,13 @@ export function VideoPostCard({post}: {post: FeedPostSliceItem}) {
<View style={[a.flex_row, a.align_center, a.gap_xs]}> <View style={[a.flex_row, a.align_center, a.gap_xs]}>
<Heart size="md" fill="white" /> <Heart size="md" fill="white" />
<Text style={[a.text_md, a.font_bold]}> <Text style={[a.text_md, a.font_bold]}>
{formatCount(i18n, post.post.likeCount || 0)} {formatCount(i18n, post.likeCount || 0)}
</Text> </Text>
</View> </View>
<View style={[a.flex_row, a.align_center, a.gap_xs]}> <View style={[a.flex_row, a.align_center, a.gap_xs]}>
<Repost size="md" fill="white" /> <Repost size="md" fill="white" />
<Text style={[a.text_md, a.font_bold]}> <Text style={[a.text_md, a.font_bold]}>
{formatCount(i18n, post.post.repostCount || 0)} {formatCount(i18n, post.repostCount || 0)}
</Text> </Text>
</View> </View>
</View> </View>
@@ -109,11 +109,7 @@ export function VideoPostCard({post}: {post: FeedPostSliceItem}) {
</View> </View>
</View> </View>
<View style={[a.pt_sm, a.flex_row, a.gap_sm, a.align_center]}> <View style={[a.pt_sm, a.flex_row, a.gap_sm, a.align_center]}>
<PreviewableUserAvatar <PreviewableUserAvatar type="user" size={24} profile={post.author} />
type="user"
size={24}
profile={post.post.author}
/>
<Text <Text
style={[ style={[
a.flex_1, a.flex_1,
@@ -123,7 +119,7 @@ export function VideoPostCard({post}: {post: FeedPostSliceItem}) {
t.atoms.text_contrast_medium, t.atoms.text_contrast_medium,
]} ]}
numberOfLines={1}> numberOfLines={1}>
{sanitizeHandle(post.post.author.handle, '@')} {sanitizeHandle(post.author.handle, '@')}
</Text> </Text>
</View> </View>
</Pressable> </Pressable>
+13 -2
View File
@@ -1,16 +1,27 @@
import {View} from 'react-native' import {View} from 'react-native'
import {AppBskyEmbedVideo} from '@atproto/api'
import {FeedPostSliceItem} from '#/state/queries/post-feed' import {FeedPostSliceItem} from '#/state/queries/post-feed'
import {atoms as a, useGutters} from '#/alf' import {atoms as a, useGutters} from '#/alf'
import {VideoPostCard} from '#/components/VideoPostCard' import {VideoPostCard} from '#/components/VideoPostCard'
export function PostFeedVideoGridRow({posts}: {posts: FeedPostSliceItem[]}) { export function PostFeedVideoGridRow({slices}: {slices: FeedPostSliceItem[]}) {
const gutters = useGutters(['base', 'base', 0, 'base']) const gutters = useGutters(['base', 'base', 0, 'base'])
const posts = slices
.filter(slice => AppBskyEmbedVideo.isView(slice.post.embed))
.map(slice => slice.post)
/**
* This should not happen because we should be filtering out posts without
* videos within the `PostFeed` component.
*/
if (posts.length !== slices.length) return null
return ( return (
<View style={[gutters]}> <View style={[gutters]}>
<View style={[a.flex_row, a.gap_lg]}> <View style={[a.flex_row, a.gap_lg]}>
{posts.map(post => ( {posts.map(post => (
<View key={post._reactKey} style={[a.flex_1]}> <View key={post.uri} style={[a.flex_1]}>
<VideoPostCard post={post} /> <VideoPostCard post={post} />
</View> </View>
))} ))}
+9 -4
View File
@@ -9,7 +9,7 @@ import {
View, View,
ViewStyle, ViewStyle,
} from 'react-native' } from 'react-native'
import {AppBskyActorDefs} from '@atproto/api' import {AppBskyActorDefs, AppBskyEmbedVideo} from '@atproto/api'
import {msg} from '@lingui/macro' import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {useQueryClient} from '@tanstack/react-query' import {useQueryClient} from '@tanstack/react-query'
@@ -93,7 +93,7 @@ type FeedRow =
| { | {
type: 'videoGridRow' type: 'videoGridRow'
key: string key: string
posts: FeedPostSliceItem[] slices: FeedPostSliceItem[]
} }
| { | {
type: 'sliceViewFullThread' type: 'sliceViewFullThread'
@@ -338,6 +338,11 @@ let PostFeed = ({
const slice = page.slices[i] const slice = page.slices[i]
const root = slice.items.at(0) const root = slice.items.at(0)
if (!root) continue if (!root) continue
// TODO test this
if (!AppBskyEmbedVideo.isView(root.post.embed)) {
i--
continue
}
const cols = gtMobile ? 3 : 2 const cols = gtMobile ? 3 : 2
if (i % cols === 0) { if (i % cols === 0) {
rows.push([root]) rows.push([root])
@@ -352,7 +357,7 @@ let PostFeed = ({
arr.push({ arr.push({
type: 'videoGridRow', type: 'videoGridRow',
key: row.map(r => r._reactKey).join('-'), key: row.map(r => r._reactKey).join('-'),
posts: row, slices: row,
}) })
} }
} else { } else {
@@ -590,7 +595,7 @@ let PostFeed = ({
} else if (row.type === 'sliceViewFullThread') { } else if (row.type === 'sliceViewFullThread') {
return <ViewFullThread uri={row.uri} /> return <ViewFullThread uri={row.uri} />
} else if (row.type === 'videoGridRow') { } else if (row.type === 'videoGridRow') {
return <PostFeedVideoGridRow posts={row.posts} /> return <PostFeedVideoGridRow slices={row.slices} />
} else { } else {
return null return null
} }