Collection of moderation fixes (#4566)

* Fix: dont blur parents in threads that embed blocks

* After tapping 'Show hidden replies', show the individual hider cards

* Add shape override to UserAvatar and fix the fallback avi for labelers

* Fix precedence

* Detect shape for DefaultAvatar

---------

Co-authored-by: Dan Abramov <dan.abramov@gmail.com>
zio/stable
Paul Frazee 2024-06-18 16:36:46 -07:00 committed by GitHub
parent ac08c76168
commit 07c2be255f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 41 additions and 14 deletions

View File

@ -92,6 +92,8 @@ function PostLabel({
<UserAvatar <UserAvatar
avatar={desc.sourceAvi} avatar={desc.sourceAvi}
size={size === 'large' ? 16 : 12} size={size === 'large' ? 16 : 12}
type="labeler"
shape="circle"
/> />
) : ( ) : (
<desc.icon size="sm" fill={t.atoms.text_contrast_medium.color} /> <desc.icon size="sm" fill={t.atoms.text_contrast_medium.color} />

View File

@ -1,6 +1,6 @@
import React, {ComponentProps} from 'react' import React, {ComponentProps} from 'react'
import {Pressable, StyleProp, StyleSheet, View, ViewStyle} from 'react-native' import {Pressable, StyleProp, StyleSheet, View, ViewStyle} from 'react-native'
import {AppBskyActorDefs, ModerationUI} from '@atproto/api' import {AppBskyActorDefs, ModerationCause, ModerationUI} from '@atproto/api'
import {msg, Trans} from '@lingui/macro' import {msg, Trans} 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'
@ -45,7 +45,8 @@ export function PostHider({
const [override, setOverride] = React.useState(false) const [override, setOverride] = React.useState(false)
const control = useModerationDetailsDialogControl() const control = useModerationDetailsDialogControl()
const blur = const blur =
modui.blurs[0] || (interpretFilterAsBlur ? modui.filters[0] : undefined) modui.blurs[0] ||
(interpretFilterAsBlur ? getBlurrableFilter(modui) : undefined)
const desc = useModerationCauseDescription(blur) const desc = useModerationCauseDescription(blur)
const onBeforePress = React.useCallback(() => { const onBeforePress = React.useCallback(() => {
@ -134,6 +135,13 @@ export function PostHider({
) )
} }
function getBlurrableFilter(modui: ModerationUI): ModerationCause | undefined {
// moderation causes get "downgraded" when they originate from embedded content
// a downgraded cause should *only* drive filtering in feeds, so we want to look
// for filters that arent downgraded
return modui.filters.find(filter => !filter.downgraded)
}
const styles = StyleSheet.create({ const styles = StyleSheet.create({
child: { child: {
borderWidth: 0, borderWidth: 0,

View File

@ -331,7 +331,11 @@ export function PostThread({
<PostThreadShowHiddenReplies <PostThreadShowHiddenReplies
type={item === SHOW_HIDDEN_REPLIES ? 'hidden' : 'muted'} type={item === SHOW_HIDDEN_REPLIES ? 'hidden' : 'muted'}
onPress={() => onPress={() =>
setHiddenRepliesState(HiddenRepliesState.ShowAndOverridePostHider) setHiddenRepliesState(
item === SHOW_HIDDEN_REPLIES
? HiddenRepliesState.Show
: HiddenRepliesState.ShowAndOverridePostHider,
)
} }
hideTopBorder={index === 0} hideTopBorder={index === 0}
/> />

View File

@ -35,6 +35,7 @@ export type UserAvatarType = 'user' | 'algo' | 'list' | 'labeler'
interface BaseUserAvatarProps { interface BaseUserAvatarProps {
type?: UserAvatarType type?: UserAvatarType
shape?: 'circle' | 'square'
size: number size: number
avatar?: string | null avatar?: string | null
} }
@ -60,12 +61,16 @@ const BLUR_AMOUNT = isWeb ? 5 : 100
let DefaultAvatar = ({ let DefaultAvatar = ({
type, type,
shape: overrideShape,
size, size,
}: { }: {
type: UserAvatarType type: UserAvatarType
shape?: 'square' | 'circle'
size: number size: number
}): React.ReactNode => { }): React.ReactNode => {
const finalShape = overrideShape ?? (type === 'user' ? 'circle' : 'square')
if (type === 'algo') { if (type === 'algo') {
// TODO: shape=circle
// Font Awesome Pro 6.4.0 by @fontawesome -https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. // Font Awesome Pro 6.4.0 by @fontawesome -https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc.
return ( return (
<Svg <Svg
@ -84,6 +89,7 @@ let DefaultAvatar = ({
) )
} }
if (type === 'list') { if (type === 'list') {
// TODO: shape=circle
// Font Awesome Pro 6.4.0 by @fontawesome -https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. // Font Awesome Pro 6.4.0 by @fontawesome -https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc.
return ( return (
<Svg <Svg
@ -117,14 +123,18 @@ let DefaultAvatar = ({
viewBox="0 0 32 32" viewBox="0 0 32 32"
fill="none" fill="none"
stroke="none"> stroke="none">
<Rect {finalShape === 'square' ? (
x="0" <Rect
y="0" x="0"
width="32" y="0"
height="32" width="32"
rx="3" height="32"
fill={tokens.color.temp_purple} rx="3"
/> fill={tokens.color.temp_purple}
/>
) : (
<Circle cx="16" cy="16" r="16" fill={tokens.color.temp_purple} />
)}
<Path <Path
d="M24 9.75L16 7L8 9.75V15.9123C8 20.8848 12 23 16 25.1579C20 23 24 20.8848 24 15.9123V9.75Z" d="M24 9.75L16 7L8 9.75V15.9123C8 20.8848 12 23 16 25.1579C20 23 24 20.8848 24 15.9123V9.75Z"
stroke="white" stroke="white"
@ -135,6 +145,7 @@ let DefaultAvatar = ({
</Svg> </Svg>
) )
} }
// TODO: shape=square
return ( return (
<Svg <Svg
testID="userAvatarFallback" testID="userAvatarFallback"
@ -159,6 +170,7 @@ export {DefaultAvatar}
let UserAvatar = ({ let UserAvatar = ({
type = 'user', type = 'user',
shape: overrideShape,
size, size,
avatar, avatar,
moderation, moderation,
@ -166,9 +178,10 @@ let UserAvatar = ({
}: UserAvatarProps): React.ReactNode => { }: UserAvatarProps): React.ReactNode => {
const pal = usePalette('default') const pal = usePalette('default')
const backgroundColor = pal.colors.backgroundLight const backgroundColor = pal.colors.backgroundLight
const finalShape = overrideShape ?? (type === 'user' ? 'circle' : 'square')
const aviStyle = useMemo(() => { const aviStyle = useMemo(() => {
if (type === 'algo' || type === 'list' || type === 'labeler') { if (finalShape === 'square') {
return { return {
width: size, width: size,
height: size, height: size,
@ -182,7 +195,7 @@ let UserAvatar = ({
borderRadius: Math.floor(size / 2), borderRadius: Math.floor(size / 2),
backgroundColor, backgroundColor,
} }
}, [type, size, backgroundColor]) }, [finalShape, size, backgroundColor])
const alert = useMemo(() => { const alert = useMemo(() => {
if (!moderation?.alert) { if (!moderation?.alert) {
@ -224,7 +237,7 @@ let UserAvatar = ({
</View> </View>
) : ( ) : (
<View style={{width: size, height: size}}> <View style={{width: size, height: size}}>
<DefaultAvatar type={type} size={size} /> <DefaultAvatar type={type} shape={finalShape} size={size} />
{alert} {alert}
</View> </View>
) )