mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
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}