Bluesky app fork with some witchin' additions 馃挮
at readme-update 257 lines 7.4 kB view raw
1import React from 'react' 2import {type StyleProp, View, type ViewStyle} from 'react-native' 3import {type ModerationUI} from '@atproto/api' 4import {msg, Trans} from '@lingui/macro' 5import {useLingui} from '@lingui/react' 6 7import {ADULT_CONTENT_LABELS, isJustAMute} from '#/lib/moderation' 8import {useGlobalLabelStrings} from '#/lib/moderation/useGlobalLabelStrings' 9import {getDefinition, getLabelStrings} from '#/lib/moderation/useLabelInfo' 10import {useModerationCauseDescription} from '#/lib/moderation/useModerationCauseDescription' 11import {sanitizeDisplayName} from '#/lib/strings/display-names' 12import {useLabelDefinitions} from '#/state/preferences' 13import {atoms as a, useBreakpoints, useTheme, web} from '#/alf' 14import {Button} from '#/components/Button' 15import { 16 ModerationDetailsDialog, 17 useModerationDetailsDialogControl, 18} from '#/components/moderation/ModerationDetailsDialog' 19import {Text} from '#/components/Typography' 20 21export function ContentHider({ 22 testID, 23 modui, 24 ignoreMute, 25 style, 26 activeStyle, 27 childContainerStyle, 28 children, 29}: { 30 testID?: string 31 modui: ModerationUI | undefined 32 ignoreMute?: boolean 33 style?: StyleProp<ViewStyle> 34 activeStyle?: StyleProp<ViewStyle> 35 childContainerStyle?: StyleProp<ViewStyle> 36 children?: React.ReactNode | ((props: {active: boolean}) => React.ReactNode) 37}) { 38 const blur = modui?.blurs[0] 39 if (!blur || (ignoreMute && isJustAMute(modui))) { 40 return ( 41 <View testID={testID} style={style}> 42 {typeof children === 'function' ? children({active: false}) : children} 43 </View> 44 ) 45 } 46 return ( 47 <ContentHiderActive 48 testID={testID} 49 modui={modui} 50 style={[style, activeStyle]} 51 childContainerStyle={childContainerStyle}> 52 {typeof children === 'function' ? children({active: true}) : children} 53 </ContentHiderActive> 54 ) 55} 56 57function ContentHiderActive({ 58 testID, 59 modui, 60 style, 61 childContainerStyle, 62 children, 63}: React.PropsWithChildren<{ 64 testID?: string 65 modui: ModerationUI 66 style?: StyleProp<ViewStyle> 67 childContainerStyle?: StyleProp<ViewStyle> 68}>) { 69 const t = useTheme() 70 const {_} = useLingui() 71 const {gtMobile} = useBreakpoints() 72 const [override, setOverride] = React.useState(false) 73 const control = useModerationDetailsDialogControl() 74 const {labelDefs} = useLabelDefinitions() 75 const globalLabelStrings = useGlobalLabelStrings() 76 const {i18n} = useLingui() 77 const blur = modui?.blurs[0] 78 const desc = useModerationCauseDescription(blur) 79 80 const labelName = React.useMemo(() => { 81 if (!modui?.blurs || !blur) { 82 return undefined 83 } 84 if ( 85 blur.type !== 'label' || 86 (blur.type === 'label' && blur.source.type !== 'user') 87 ) { 88 if (desc.isSubjectAccount) { 89 return _(msg`${desc.name} (Account)`) 90 } else { 91 return desc.name 92 } 93 } 94 95 let hasAdultContentLabel = false 96 const selfBlurNames = modui.blurs 97 .filter(cause => { 98 if (cause.type !== 'label') { 99 return false 100 } 101 if (cause.source.type !== 'user') { 102 return false 103 } 104 if (ADULT_CONTENT_LABELS.includes(cause.label.val)) { 105 if (hasAdultContentLabel) { 106 return false 107 } 108 hasAdultContentLabel = true 109 } 110 return true 111 }) 112 .slice(0, 2) 113 .map(cause => { 114 if (cause.type !== 'label') { 115 return 116 } 117 118 const def = cause.labelDef || getDefinition(labelDefs, cause.label) 119 if (def.identifier === 'porn' || def.identifier === 'sexual') { 120 return _(msg`Adult Content`) 121 } 122 return getLabelStrings(i18n.locale, globalLabelStrings, def).name 123 }) 124 125 if (selfBlurNames.length === 0) { 126 return desc.name 127 } 128 return [...new Set(selfBlurNames)].join(', ') 129 }, [ 130 _, 131 modui?.blurs, 132 blur, 133 desc.name, 134 desc.isSubjectAccount, 135 labelDefs, 136 i18n.locale, 137 globalLabelStrings, 138 ]) 139 140 return ( 141 <View testID={testID} style={[a.overflow_hidden, style]}> 142 <ModerationDetailsDialog control={control} modcause={blur} /> 143 144 <Button 145 onPress={e => { 146 e.preventDefault() 147 e.stopPropagation() 148 if (!modui.noOverride) { 149 setOverride(v => !v) 150 } else { 151 control.open() 152 } 153 }} 154 label={desc.name} 155 accessibilityHint={ 156 modui.noOverride 157 ? _(msg`Learn more about the moderation applied to this content`) 158 : override 159 ? _(msg`Hides the content`) 160 : _(msg`Shows the content`) 161 }> 162 {state => ( 163 <View 164 style={[ 165 a.flex_row, 166 a.w_full, 167 a.justify_start, 168 a.align_center, 169 a.py_md, 170 a.px_lg, 171 a.gap_xs, 172 a.rounded_sm, 173 t.atoms.bg_contrast_25, 174 gtMobile && [a.gap_sm, a.py_lg, a.mt_xs, a.px_xl], 175 (state.hovered || state.pressed) && t.atoms.bg_contrast_50, 176 ]}> 177 <desc.icon 178 size="md" 179 fill={t.atoms.text_contrast_medium.color} 180 style={{marginLeft: -2}} 181 /> 182 <Text 183 style={[ 184 a.flex_1, 185 a.text_left, 186 a.font_semi_bold, 187 a.leading_snug, 188 gtMobile && [a.font_semi_bold], 189 t.atoms.text_contrast_medium, 190 web({ 191 marginBottom: 1, 192 }), 193 ]} 194 numberOfLines={2}> 195 {labelName} 196 </Text> 197 {!modui.noOverride && ( 198 <Text 199 style={[ 200 a.font_semi_bold, 201 a.leading_snug, 202 gtMobile && [a.font_semi_bold], 203 t.atoms.text_contrast_high, 204 web({ 205 marginBottom: 1, 206 }), 207 ]}> 208 {override ? <Trans>Hide</Trans> : <Trans>Show</Trans>} 209 </Text> 210 )} 211 </View> 212 )} 213 </Button> 214 215 {desc.source && blur.type === 'label' && !override && ( 216 <Button 217 onPress={e => { 218 e.preventDefault() 219 e.stopPropagation() 220 control.open() 221 }} 222 label={_( 223 msg`Learn more about the moderation applied to this content`, 224 )} 225 style={[a.pt_sm]}> 226 {state => ( 227 <Text 228 style={[ 229 a.flex_1, 230 a.text_sm, 231 a.font_normal, 232 a.leading_snug, 233 t.atoms.text_contrast_medium, 234 a.text_left, 235 ]}> 236 {desc.sourceType === 'user' ? ( 237 <Trans>Labeled by the author.</Trans> 238 ) : ( 239 <Trans>Labeled by {sanitizeDisplayName(desc.source!)}.</Trans> 240 )}{' '} 241 <Text 242 style={[ 243 {color: t.palette.primary_500}, 244 a.text_sm, 245 state.hovered && [web({textDecoration: 'underline'})], 246 ]}> 247 <Trans>Learn more.</Trans> 248 </Text> 249 </Text> 250 )} 251 </Button> 252 )} 253 254 {override && <View style={childContainerStyle}>{children}</View>} 255 </View> 256 ) 257}