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