Filter out posts without videos
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -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,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
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user