mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
at samuel/exp-cli 297 lines 8.6 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_sm, 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 emoji style={[a.font_bold, gtPhone ? a.text_sm : a.text_md]}> 50 {name} 51 </Text> 52 <Text emoji style={[t.atoms.text_contrast_medium, a.leading_snug]}> 53 {description} 54 </Text> 55 56 {children} 57 </View> 58 ) 59} 60 61export function Buttons({ 62 name, 63 values, 64 onChange, 65 ignoreLabel, 66 warnLabel, 67 hideLabel, 68}: { 69 name: string 70 values: ToggleButton.GroupProps['values'] 71 onChange: ToggleButton.GroupProps['onChange'] 72 ignoreLabel?: string 73 warnLabel?: string 74 hideLabel?: string 75}) { 76 const {_} = useLingui() 77 78 return ( 79 <View style={[{minHeight: 35}, a.w_full]}> 80 <ToggleButton.Group 81 label={_( 82 msg`Configure content filtering setting for category: ${name}`, 83 )} 84 values={values} 85 onChange={onChange}> 86 {ignoreLabel && ( 87 <ToggleButton.Button name="ignore" label={ignoreLabel}> 88 <ToggleButton.ButtonText>{ignoreLabel}</ToggleButton.ButtonText> 89 </ToggleButton.Button> 90 )} 91 {warnLabel && ( 92 <ToggleButton.Button name="warn" label={warnLabel}> 93 <ToggleButton.ButtonText>{warnLabel}</ToggleButton.ButtonText> 94 </ToggleButton.Button> 95 )} 96 {hideLabel && ( 97 <ToggleButton.Button name="hide" label={hideLabel}> 98 <ToggleButton.ButtonText>{hideLabel}</ToggleButton.ButtonText> 99 </ToggleButton.Button> 100 )} 101 </ToggleButton.Group> 102 </View> 103 ) 104} 105 106/** 107 * For use on the global Moderation screen to set prefs for a "global" label, 108 * not scoped to a single labeler. 109 */ 110export function GlobalLabelPreference({ 111 labelDefinition, 112 disabled, 113}: { 114 labelDefinition: InterpretedLabelValueDefinition 115 disabled?: boolean 116}) { 117 const {_} = useLingui() 118 119 const {identifier} = labelDefinition 120 const {data: preferences} = usePreferencesQuery() 121 const {mutate, variables} = usePreferencesSetContentLabelMutation() 122 const savedPref = preferences?.moderationPrefs.labels[identifier] 123 const pref = variables?.visibility ?? savedPref ?? 'warn' 124 125 const allLabelStrings = useGlobalLabelStrings() 126 const labelStrings = 127 labelDefinition.identifier in allLabelStrings 128 ? allLabelStrings[labelDefinition.identifier] 129 : { 130 name: labelDefinition.identifier, 131 description: `Labeled "${labelDefinition.identifier}"`, 132 } 133 134 const labelOptions = { 135 hide: _(msg`Hide`), 136 warn: _(msg`Warn`), 137 ignore: _(msg`Show`), 138 } 139 140 return ( 141 <Outer> 142 <Content 143 name={labelStrings.name} 144 description={labelStrings.description} 145 /> 146 {!disabled && ( 147 <Buttons 148 name={labelStrings.name.toLowerCase()} 149 values={[pref]} 150 onChange={values => { 151 mutate({ 152 label: identifier, 153 visibility: values[0] as LabelPreference, 154 labelerDid: undefined, 155 }) 156 }} 157 ignoreLabel={labelOptions.ignore} 158 warnLabel={labelOptions.warn} 159 hideLabel={labelOptions.hide} 160 /> 161 )} 162 </Outer> 163 ) 164} 165 166/** 167 * For use on individual labeler pages 168 */ 169export function LabelerLabelPreference({ 170 labelDefinition, 171 disabled, 172 labelerDid, 173}: { 174 labelDefinition: InterpretedLabelValueDefinition 175 disabled?: boolean 176 labelerDid?: string 177}) { 178 const {_, i18n} = useLingui() 179 const t = useTheme() 180 const {gtPhone} = useBreakpoints() 181 182 const isGlobalLabel = !labelDefinition.definedBy 183 const {identifier} = labelDefinition 184 const {data: preferences} = usePreferencesQuery() 185 const {mutate, variables} = usePreferencesSetContentLabelMutation() 186 const savedPref = 187 labelerDid && !isGlobalLabel 188 ? preferences?.moderationPrefs.labelers.find(l => l.did === labelerDid) 189 ?.labels[identifier] 190 : preferences?.moderationPrefs.labels[identifier] 191 const pref = 192 variables?.visibility ?? 193 savedPref ?? 194 labelDefinition.defaultSetting ?? 195 'warn' 196 197 // does the 'warn' setting make sense for this label? 198 const canWarn = !( 199 labelDefinition.blurs === 'none' && labelDefinition.severity === 'none' 200 ) 201 // is this label adult only? 202 const adultOnly = labelDefinition.flags.includes('adult') 203 // is this label disabled because it's adult only? 204 const adultDisabled = 205 adultOnly && !preferences?.moderationPrefs.adultContentEnabled 206 // are there any reasons we cant configure this label here? 207 const cantConfigure = isGlobalLabel || adultDisabled 208 const showConfig = !disabled && (gtPhone || !cantConfigure) 209 210 // adjust the pref based on whether warn is available 211 let prefAdjusted = pref 212 if (adultDisabled) { 213 prefAdjusted = 'hide' 214 } else if (!canWarn && pref === 'warn') { 215 prefAdjusted = 'ignore' 216 } 217 218 // grab localized descriptions of the label and its settings 219 const currentPrefLabel = useLabelBehaviorDescription( 220 labelDefinition, 221 prefAdjusted, 222 ) 223 const hideLabel = useLabelBehaviorDescription(labelDefinition, 'hide') 224 const warnLabel = useLabelBehaviorDescription(labelDefinition, 'warn') 225 const ignoreLabel = useLabelBehaviorDescription(labelDefinition, 'ignore') 226 const globalLabelStrings = useGlobalLabelStrings() 227 const labelStrings = getLabelStrings( 228 i18n.locale, 229 globalLabelStrings, 230 labelDefinition, 231 ) 232 233 return ( 234 <Outer> 235 <Content name={labelStrings.name} description={labelStrings.description}> 236 {cantConfigure && ( 237 <View style={[a.flex_row, a.gap_xs, a.align_center, a.mt_xs]}> 238 <CircleInfo size="sm" fill={t.atoms.text_contrast_high.color} /> 239 240 <Text style={[t.atoms.text_contrast_medium, a.font_bold, 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 <> 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 a.self_start, 272 ]}> 273 <Text emoji style={[a.font_bold, t.atoms.text_contrast_low]}> 274 {currentPrefLabel} 275 </Text> 276 </View> 277 ) : ( 278 <Buttons 279 name={labelStrings.name.toLowerCase()} 280 values={[pref]} 281 onChange={values => { 282 mutate({ 283 label: identifier, 284 visibility: values[0] as LabelPreference, 285 labelerDid, 286 }) 287 }} 288 ignoreLabel={ignoreLabel} 289 warnLabel={canWarn ? warnLabel : undefined} 290 hideLabel={hideLabel} 291 /> 292 )} 293 </> 294 )} 295 </Outer> 296 ) 297}