Add content hider to video card
This commit is contained in:
@@ -1,30 +1,40 @@
|
|||||||
import {View} from 'react-native'
|
import {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, AppBskyFeedDefs, AppBskyFeedPost} from '@atproto/api'
|
import {
|
||||||
|
AppBskyActorDefs,
|
||||||
|
AppBskyEmbedVideo,
|
||||||
|
AppBskyFeedDefs,
|
||||||
|
AppBskyFeedPost,
|
||||||
|
ModerationDecision,
|
||||||
|
} 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 {formatCount} from '#/view/com/util/numeric/format'
|
import {formatCount} from '#/view/com/util/numeric/format'
|
||||||
import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar'
|
import {UserAvatar} from '#/view/com/util/UserAvatar'
|
||||||
import {VideoFeedSourceContext} from '#/screens/VideoFeed/types'
|
import {VideoFeedSourceContext} from '#/screens/VideoFeed/types'
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
import {BLUE_HUE} from '#/alf/util/colorGeneration'
|
import {BLUE_HUE} from '#/alf/util/colorGeneration'
|
||||||
import {select} from '#/alf/util/themeSelector'
|
import {select} from '#/alf/util/themeSelector'
|
||||||
import {useInteractionState} from '#/components/hooks/useInteractionState'
|
import {useInteractionState} from '#/components/hooks/useInteractionState'
|
||||||
|
import {EyeSlash_Stroke2_Corner0_Rounded as Eye} from '#/components/icons/EyeSlash'
|
||||||
import {Heart2_Stroke2_Corner0_Rounded as Heart} from '#/components/icons/Heart2'
|
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 {Link} from '#/components/Link'
|
import {Link} from '#/components/Link'
|
||||||
import {MediaInsetBorder} from '#/components/MediaInsetBorder'
|
import {MediaInsetBorder} from '#/components/MediaInsetBorder'
|
||||||
|
import * as Hider from '#/components/moderation/Hider'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
|
|
||||||
export function VideoPostCard({
|
export function VideoPostCard({
|
||||||
post,
|
post,
|
||||||
sourceContext,
|
sourceContext,
|
||||||
|
moderation,
|
||||||
}: {
|
}: {
|
||||||
post: AppBskyFeedDefs.PostView
|
post: AppBskyFeedDefs.PostView
|
||||||
sourceContext: VideoFeedSourceContext
|
sourceContext: VideoFeedSourceContext
|
||||||
|
moderation: ModerationDecision
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_, i18n} = useLingui()
|
const {_, i18n} = useLingui()
|
||||||
@@ -70,6 +80,50 @@ export function VideoPostCard({
|
|||||||
justifyContent: undefined,
|
justifyContent: undefined,
|
||||||
},
|
},
|
||||||
]}>
|
]}>
|
||||||
|
<Hider.Outer modui={moderation.ui('contentList')}>
|
||||||
|
<Hider.Mask>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.justify_center,
|
||||||
|
a.rounded_md,
|
||||||
|
a.overflow_hidden,
|
||||||
|
{
|
||||||
|
backgroundColor: black,
|
||||||
|
aspectRatio: 9 / 16,
|
||||||
|
},
|
||||||
|
]}>
|
||||||
|
<Image
|
||||||
|
source={{uri: thumbnail}}
|
||||||
|
style={[a.w_full, a.h_full, {opacity: pressed ? 0.8 : 1}]}
|
||||||
|
accessibilityIgnoresInvertColors
|
||||||
|
blurRadius={40}
|
||||||
|
/>
|
||||||
|
<MediaInsetBorder />
|
||||||
|
<View
|
||||||
|
style={[a.absolute, a.inset_0, a.justify_center, a.align_center]}>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.absolute,
|
||||||
|
a.inset_0,
|
||||||
|
a.justify_center,
|
||||||
|
a.align_center,
|
||||||
|
{
|
||||||
|
backgroundColor: 'black',
|
||||||
|
opacity: 0.2,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View style={[a.align_center, a.gap_xs]}>
|
||||||
|
<Eye size="lg" fill="white" />
|
||||||
|
<Text style={[a.text_sm, {color: 'white'}]}>
|
||||||
|
{_(msg`Hidden`)}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<VideoPostCardTextPlaceholder author={post.author} />
|
||||||
|
</Hider.Mask>
|
||||||
|
<Hider.Content>
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
a.justify_center,
|
a.justify_center,
|
||||||
@@ -105,7 +159,8 @@ export function VideoPostCard({
|
|||||||
style={[a.absolute, a.inset_0, {opacity: 0.9}]}
|
style={[a.absolute, a.inset_0, {opacity: 0.9}]}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<View style={[a.relative, a.z_10, a.p_md, a.flex_row, a.gap_md]}>
|
<View
|
||||||
|
style={[a.relative, a.z_10, a.p_md, a.flex_row, a.gap_md]}>
|
||||||
{likeCount > 0 && (
|
{likeCount > 0 && (
|
||||||
<View style={[a.flex_row, a.align_center, a.gap_xs]}>
|
<View style={[a.flex_row, a.align_center, a.gap_xs]}>
|
||||||
<Heart size="sm" fill="white" />
|
<Heart size="sm" fill="white" />
|
||||||
@@ -133,7 +188,7 @@ export function VideoPostCard({
|
|||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
<View style={[a.flex_row, a.gap_xs, a.align_center]}>
|
<View style={[a.flex_row, a.gap_xs, a.align_center]}>
|
||||||
<PreviewableUserAvatar type="user" size={20} profile={post.author} />
|
<UserAvatar type="user" size={20} avatar={post.author.avatar} />
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
a.flex_1,
|
a.flex_1,
|
||||||
@@ -146,6 +201,8 @@ export function VideoPostCard({
|
|||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
</Hider.Content>
|
||||||
|
</Hider.Outer>
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -171,6 +228,20 @@ export function VideoPostCardPlaceholder() {
|
|||||||
]}>
|
]}>
|
||||||
<MediaInsetBorder />
|
<MediaInsetBorder />
|
||||||
</View>
|
</View>
|
||||||
|
<VideoPostCardTextPlaceholder />
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function VideoPostCardTextPlaceholder({
|
||||||
|
author,
|
||||||
|
}: {
|
||||||
|
author?: AppBskyActorDefs.ProfileViewBasic
|
||||||
|
}) {
|
||||||
|
const t = useTheme()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={[a.flex_1]}>
|
||||||
<View style={[a.pr_xs, {paddingTop: 8, gap: 6}]}>
|
<View style={[a.pr_xs, {paddingTop: 8, gap: 6}]}>
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
@@ -193,6 +264,21 @@ export function VideoPostCardPlaceholder() {
|
|||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
{author ? (
|
||||||
|
<View style={[a.flex_row, a.gap_xs, a.align_center]}>
|
||||||
|
<UserAvatar type="user" size={20} avatar={author.avatar} />
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
a.text_sm,
|
||||||
|
a.leading_tight,
|
||||||
|
t.atoms.text_contrast_medium,
|
||||||
|
]}
|
||||||
|
numberOfLines={1}>
|
||||||
|
{sanitizeHandle(author.handle, '@')}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
<View style={[a.flex_row, a.gap_xs, a.align_center]}>
|
<View style={[a.flex_row, a.gap_xs, a.align_center]}>
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
@@ -215,6 +301,7 @@ export function VideoPostCardPlaceholder() {
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -19,7 +19,10 @@ export function PostFeedVideoGridRow({
|
|||||||
const gutters = useGutters(['base', 'base', 0, 'base'])
|
const gutters = useGutters(['base', 'base', 0, 'base'])
|
||||||
const posts = slices
|
const posts = slices
|
||||||
.filter(slice => AppBskyEmbedVideo.isView(slice.post.embed))
|
.filter(slice => AppBskyEmbedVideo.isView(slice.post.embed))
|
||||||
.map(slice => slice.post)
|
.map(slice => ({
|
||||||
|
post: slice.post,
|
||||||
|
moderation: slice.moderation,
|
||||||
|
}))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This should not happen because we should be filtering out posts without
|
* This should not happen because we should be filtering out posts without
|
||||||
@@ -31,8 +34,12 @@ export function PostFeedVideoGridRow({
|
|||||||
<View style={[gutters]}>
|
<View style={[gutters]}>
|
||||||
<View style={[a.flex_row, a.gap_sm]}>
|
<View style={[a.flex_row, a.gap_sm]}>
|
||||||
{posts.map(post => (
|
{posts.map(post => (
|
||||||
<View key={post.uri} style={[a.flex_1]}>
|
<View key={post.post.uri} style={[a.flex_1]}>
|
||||||
<VideoPostCard post={post} sourceContext={sourceContext} />
|
<VideoPostCard
|
||||||
|
post={post.post}
|
||||||
|
sourceContext={sourceContext}
|
||||||
|
moderation={post.moderation}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
Reference in New Issue
Block a user