mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
at mod-auth 296 lines 8.7 kB view raw
1import React from 'react' 2import {View} from 'react-native' 3import {InterpretedLabelValueDefinition, LabelPreference} from '@atproto/api' 4import {msg, Trans} from '@lingui/macro' 5import {useLingui} from '@lingui/react' 6 7import {useGlobalLabelStrings} from '#/lib/moderation/useGlobalLabelStrings' 8import {useLabelBehaviorDescription} from '#/lib/moderation/useLabelBehaviorDescription' 9import {getLabelStrings} from '#/lib/moderation/useLabelInfo' 10import { 11 usePreferencesQuery, 12 usePreferencesSetContentLabelMutation, 13} from '#/state/queries/preferences' 14import {atoms as a, useBreakpoints, useTheme} from '#/alf' 15import * as ToggleButton from '#/components/forms/ToggleButton' 16import {InlineLinkText} from '#/components/Link' 17import {Text} from '#/components/Typography' 18import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '../icons/CircleInfo' 19 20export function Outer({children}: React.PropsWithChildren<{}>) { 21 return ( 22 <View 23 style={[ 24 a.flex_row, 25 a.gap_md, 26 a.px_lg, 27 a.py_lg, 28 a.justify_between, 29 a.flex_wrap, 30 ]}> 31 {children} 32 </View> 33 ) 34} 35 36export function Content({ 37 children, 38 name, 39 description, 40}: React.PropsWithChildren<{ 41 name: string 42 description: string 43}>) { 44 const t = useTheme() 45 const {gtPhone} = useBreakpoints() 46 47 return ( 48 <View style={[a.gap_xs, a.flex_1]}> 49 <Text style={[a.font_bold, gtPhone ? a.text_sm : a.text_md]}>{name}</Text> 50 <Text style={[t.atoms.text_contrast_medium, a.leading_snug]}> 51 {description} 52 </Text> 53 54 {children} 55 </View> 56 ) 57} 58 59export function Buttons({ 60 name, 61 values, 62 onChange, 63 ignoreLabel, 64 warnLabel, 65 hideLabel, 66}: { 67 name: string 68 values: ToggleButton.GroupProps['values'] 69 onChange: ToggleButton.GroupProps['onChange'] 70 ignoreLabel?: string 71 warnLabel?: string 72 hideLabel?: string 73}) { 74 const {_} = useLingui() 75 const {gtPhone} = useBreakpoints() 76 77 return ( 78 <View style={[{minHeight: 35}, gtPhone ? undefined : a.w_full]}> 79 <ToggleButton.Group 80 label={_( 81 msg`Configure content filtering setting for category: ${name}`, 82 )} 83 values={values} 84 onChange={onChange}> 85 {ignoreLabel && ( 86 <ToggleButton.Button name="ignore" label={ignoreLabel}> 87 <ToggleButton.ButtonText>{ignoreLabel}</ToggleButton.ButtonText> 88 </ToggleButton.Button> 89 )} 90 {warnLabel && ( 91 <ToggleButton.Button name="warn" label={warnLabel}> 92 <ToggleButton.ButtonText>{warnLabel}</ToggleButton.ButtonText> 93 </ToggleButton.Button> 94 )} 95 {hideLabel && ( 96 <ToggleButton.Button name="hide" label={hideLabel}> 97 <ToggleButton.ButtonText>{hideLabel}</ToggleButton.ButtonText> 98 </ToggleButton.Button> 99 )} 100 </ToggleButton.Group> 101 </View> 102 ) 103} 104 105/** 106 * For use on the global Moderation screen to set prefs for a "global" label, 107 * not scoped to a single labeler. 108 */ 109export function GlobalLabelPreference({ 110 labelDefinition, 111 disabled, 112}: { 113 labelDefinition: InterpretedLabelValueDefinition 114 disabled?: boolean 115}) { 116 const {_} = useLingui() 117 118 const {identifier} = labelDefinition 119 const {data: preferences} = usePreferencesQuery() 120 const {mutate, variables} = usePreferencesSetContentLabelMutation() 121 const savedPref = preferences?.moderationPrefs.labels[identifier] 122 const pref = variables?.visibility ?? savedPref ?? 'warn' 123 124 const allLabelStrings = useGlobalLabelStrings() 125 const labelStrings = 126 labelDefinition.identifier in allLabelStrings 127 ? allLabelStrings[labelDefinition.identifier] 128 : { 129 name: labelDefinition.identifier, 130 description: `Labeled "${labelDefinition.identifier}"`, 131 } 132 133 const labelOptions = { 134 hide: _(msg`Hide`), 135 warn: _(msg`Warn`), 136 ignore: _(msg`Show`), 137 } 138 139 return ( 140 <Outer> 141 <Content 142 name={labelStrings.name} 143 description={labelStrings.description} 144 /> 145 {!disabled && ( 146 <Buttons 147 name={labelStrings.name.toLowerCase()} 148 values={[pref]} 149 onChange={values => { 150 mutate({ 151 label: identifier, 152 visibility: values[0] as LabelPreference, 153 labelerDid: undefined, 154 }) 155 }} 156 ignoreLabel={labelOptions.ignore} 157 warnLabel={labelOptions.warn} 158 hideLabel={labelOptions.hide} 159 /> 160 )} 161 </Outer> 162 ) 163} 164 165/** 166 * For use on individual labeler pages 167 */ 168export function LabelerLabelPreference({ 169 labelDefinition, 170 disabled, 171 labelerDid, 172}: { 173 labelDefinition: InterpretedLabelValueDefinition 174 disabled?: boolean 175 labelerDid?: string 176}) { 177 const {_, i18n} = useLingui() 178 const t = useTheme() 179 const {gtPhone} = useBreakpoints() 180 181 const isGlobalLabel = !labelDefinition.definedBy 182 const {identifier} = labelDefinition 183 const {data: preferences} = usePreferencesQuery() 184 const {mutate, variables} = usePreferencesSetContentLabelMutation() 185 const savedPref = 186 labelerDid && !isGlobalLabel 187 ? preferences?.moderationPrefs.labelers.find(l => l.did === labelerDid) 188 ?.labels[identifier] 189 : preferences?.moderationPrefs.labels[identifier] 190 const pref = 191 variables?.visibility ?? 192 savedPref ?? 193 labelDefinition.defaultSetting ?? 194 'warn' 195 196 // does the 'warn' setting make sense for this label? 197 const canWarn = !( 198 labelDefinition.blurs === 'none' && labelDefinition.severity === 'none' 199 ) 200 // is this label adult only? 201 const adultOnly = labelDefinition.flags.includes('adult') 202 // is this label disabled because it's adult only? 203 const adultDisabled = 204 adultOnly && !preferences?.moderationPrefs.adultContentEnabled 205 // are there any reasons we cant configure this label here? 206 const cantConfigure = isGlobalLabel || adultDisabled 207 const showConfig = !disabled && (gtPhone || !cantConfigure) 208 209 // adjust the pref based on whether warn is available 210 let prefAdjusted = pref 211 if (adultDisabled) { 212 prefAdjusted = 'hide' 213 } else if (!canWarn && pref === 'warn') { 214 prefAdjusted = 'ignore' 215 } 216 217 // grab localized descriptions of the label and its settings 218 const currentPrefLabel = useLabelBehaviorDescription( 219 labelDefinition, 220 prefAdjusted, 221 ) 222 const hideLabel = useLabelBehaviorDescription(labelDefinition, 'hide') 223 const warnLabel = useLabelBehaviorDescription(labelDefinition, 'warn') 224 const ignoreLabel = useLabelBehaviorDescription(labelDefinition, 'ignore') 225 const globalLabelStrings = useGlobalLabelStrings() 226 const labelStrings = getLabelStrings( 227 i18n.locale, 228 globalLabelStrings, 229 labelDefinition, 230 ) 231 232 return ( 233 <Outer> 234 <Content name={labelStrings.name} description={labelStrings.description}> 235 {cantConfigure && ( 236 <View style={[a.flex_row, a.gap_xs, a.align_center, a.mt_xs]}> 237 <CircleInfo size="sm" fill={t.atoms.text_contrast_high.color} /> 238 239 <Text 240 style={[t.atoms.text_contrast_medium, a.font_semibold, a.italic]}> 241 {adultDisabled ? ( 242 <Trans>Adult content is disabled.</Trans> 243 ) : isGlobalLabel ? ( 244 <Trans> 245 Configured in{' '} 246 <InlineLinkText 247 label={_(msg`moderation settings`)} 248 to="/moderation" 249 style={a.text_sm}> 250 moderation settings 251 </InlineLinkText> 252 . 253 </Trans> 254 ) : null} 255 </Text> 256 </View> 257 )} 258 </Content> 259 260 {showConfig && ( 261 <View style={[gtPhone ? undefined : a.w_full]}> 262 {cantConfigure ? ( 263 <View 264 style={[ 265 {minHeight: 35}, 266 a.px_md, 267 a.py_md, 268 a.rounded_sm, 269 a.border, 270 t.atoms.border_contrast_low, 271 ]}> 272 <Text style={[a.font_bold, t.atoms.text_contrast_low]}> 273 {currentPrefLabel} 274 </Text> 275 </View> 276 ) : ( 277 <Buttons 278 name={labelStrings.name.toLowerCase()} 279 values={[pref]} 280 onChange={values => { 281 mutate({ 282 label: identifier, 283 visibility: values[0] as LabelPreference, 284 labelerDid, 285 }) 286 }} 287 ignoreLabel={ignoreLabel} 288 warnLabel={canWarn ? warnLabel : undefined} 289 hideLabel={hideLabel} 290 /> 291 )} 292 </View> 293 )} 294 </Outer> 295 ) 296}