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