Rework label pref ui

This commit is contained in:
Paul Frazee
2024-03-04 22:02:18 -08:00
parent 19c0ae80eb
commit a4ad364781
4 changed files with 171 additions and 274 deletions
@@ -1,133 +0,0 @@
import React from 'react'
import {Pressable} from 'react-native'
import {useLingui} from '@lingui/react'
import {msg, Trans} from '@lingui/macro'
import {InterprettedLabelValueDefinition, LabelPreference} from '@atproto/api'
import {
useLabelBehaviorDescription,
useLabelLongBehaviorDescription,
} from '#/lib/moderation/useLabelBehaviorDescription'
import {useTheme, atoms as a} from '#/alf'
import * as Dialog from '#/components/Dialog'
import {Text} from '#/components/Typography'
import {Button, ButtonText, ButtonIcon} from '#/components/Button'
import {ArrowTriangleBottom_Stroke2_Corner1_Rounded as ArrowTriangleBottom} from '../icons/ArrowTriangle'
import {Check_Stroke2_Corner0_Rounded as Check} from '../icons/Check'
export function PreferenceButton({
name,
pref,
labelValueDefinition,
onSelectPref,
}: {
name: string
pref: LabelPreference
labelValueDefinition: InterprettedLabelValueDefinition
onSelectPref: (pref: LabelPreference) => void
}) {
const {_} = useLingui()
const t = useTheme()
const control = Dialog.useDialogControl()
const settingDesc = useLabelBehaviorDescription(labelValueDefinition, pref)
const hideLabel = useLabelLongBehaviorDescription(
labelValueDefinition,
'hide',
)
const warnLabel = useLabelLongBehaviorDescription(
labelValueDefinition,
'warn',
)
const ignoreLabel = useLabelLongBehaviorDescription(
labelValueDefinition,
'ignore',
)
const canWarn = !(
labelValueDefinition.blurs === 'none' &&
labelValueDefinition.severity === 'none'
)
return (
<>
<Pressable
onPress={() => control.open()}
accessibilityLabel={settingDesc}
accessibilityHint=""
style={[
a.flex_row,
a.align_center,
a.justify_end,
a.gap_xs,
a.py_xs,
a.rounded_2xs,
]}>
<Text style={[{color: t.palette.primary_500}, a.font_semibold]}>
{settingDesc}
</Text>
<ArrowTriangleBottom width={8} fill={t.palette.primary_500} />
</Pressable>
<Dialog.Outer control={control}>
<Dialog.Handle />
<Dialog.Inner
label={_(msg`Settings: ${labelValueDefinition.identifier}`)}
style={[a.gap_sm]}>
<Text style={[a.text_2xl, a.font_bold, a.pb_xs, t.atoms.text]}>
{name}
</Text>
<Text style={[a.text_md, a.pb_sm, t.atoms.text_contrast_medium]}>
<Trans>Choose how this label should be handled.</Trans>
</Text>
<Button
label={hideLabel}
size="large"
variant="solid"
color={pref === 'hide' ? 'primary' : 'secondary'}
onPress={() => {
onSelectPref('hide')
control.close()
}}>
<ButtonText style={[a.flex_1, a.text_left]}>{hideLabel}</ButtonText>
{pref === 'hide' && <ButtonIcon icon={Check} position="right" />}
</Button>
{canWarn && (
<Button
label={warnLabel}
size="large"
variant="solid"
color={pref === 'warn' ? 'primary' : 'secondary'}
onPress={() => {
onSelectPref('warn')
control.close()
}}>
<ButtonText style={[a.flex_1, a.text_left]}>
{warnLabel}
</ButtonText>
{pref === 'warn' && <ButtonIcon icon={Check} position="right" />}
</Button>
)}
<Button
label={ignoreLabel}
size="large"
variant="solid"
color={pref === 'ignore' ? 'primary' : 'secondary'}
onPress={() => {
onSelectPref('ignore')
control.close()
}}>
<ButtonText style={[a.flex_1, a.text_left]}>
{ignoreLabel}
</ButtonText>
{pref === 'ignore' && <ButtonIcon icon={Check} position="right" />}
</Button>
</Dialog.Inner>
</Dialog.Outer>
</>
)
}
@@ -1,102 +0,0 @@
import React from 'react'
import {View} from 'react-native'
import {useLingui} from '@lingui/react'
import {msg} from '@lingui/macro'
import {InterprettedLabelValueDefinition, LabelPreference} from '@atproto/api'
import {useLabelBehaviorDescription} from '#/lib/moderation/useLabelBehaviorDescription'
import {
NativeDropdown,
DropdownItem,
} from '#/view/com/util/forms/NativeDropdown'
import {useTheme, atoms as a} from '#/alf'
import {Text} from '#/components/Typography'
import {ArrowTriangleBottom_Stroke2_Corner1_Rounded as ArrowTriangleBottom} from '../icons/ArrowTriangle'
import {useInteractionState} from '#/components/hooks/useInteractionState'
const CHECK_ICON: DropdownItem['icon'] = {
web: ['fas', 'check'],
ios: {name: 'trash'}, //doesnt matter
android: '',
}
export function PreferenceButton({
pref,
labelValueDefinition,
onSelectPref,
}: {
pref: LabelPreference
labelValueDefinition: InterprettedLabelValueDefinition
onSelectPref: (pref: LabelPreference) => void
}) {
const {_} = useLingui()
const t = useTheme()
const {
state: hovered,
onIn: onHoverIn,
onOut: onHoverOut,
} = useInteractionState()
const settingDesc = useLabelBehaviorDescription(labelValueDefinition, pref)
const hideLabel = useLabelBehaviorDescription(labelValueDefinition, 'hide')
const warnLabel = useLabelBehaviorDescription(labelValueDefinition, 'warn')
const ignoreLabel = useLabelBehaviorDescription(
labelValueDefinition,
'ignore',
)
const canWarn = !(
labelValueDefinition.blurs === 'none' &&
labelValueDefinition.severity === 'none'
)
const dropdownItems: DropdownItem[] = []
dropdownItems.push({
icon: pref === 'hide' ? CHECK_ICON : undefined,
label: hideLabel,
onPress: () => onSelectPref('hide'),
})
if (canWarn) {
dropdownItems.push({
icon: pref === 'warn' ? CHECK_ICON : undefined,
label: warnLabel,
onPress: () => onSelectPref('warn'),
})
}
dropdownItems.push({
icon: pref === 'ignore' ? CHECK_ICON : undefined,
label: ignoreLabel,
onPress: () => onSelectPref('ignore'),
})
return (
<NativeDropdown
items={dropdownItems}
accessibilityLabel={_(msg`More post options`)}
accessibilityHint="">
<View
style={[
a.flex_row,
a.align_center,
a.justify_end,
a.gap_xs,
a.py_xs,
a.rounded_2xs,
hovered && {
// @ts-ignore
textDecorationLine: 'underline',
textDecorationColor: t.palette.primary_500,
},
]}
// @ts-ignore
onMouseEnter={onHoverIn}
onMouseLeave={onHoverOut}>
<Text style={[{color: t.palette.primary_500}, a.font_semibold]}>
{settingDesc}
</Text>
<ArrowTriangleBottom width={8} fill={t.palette.primary_500} />
</View>
</NativeDropdown>
)
}
+163 -24
View File
@@ -1,8 +1,14 @@
import React from 'react'
import {View} from 'react-native'
import {Pressable, View} from 'react-native'
import {InterprettedLabelValueDefinition, LabelPreference} from '@atproto/api'
import {useLingui} from '@lingui/react'
import {msg, Trans} from '@lingui/macro'
import {useLabelStrings} from '#/lib/moderation/useLabelStrings'
import {
useLabelBehaviorDescription,
useLabelLongBehaviorDescription,
} from '#/lib/moderation/useLabelBehaviorDescription'
import {
usePreferencesQuery,
usePreferencesSetContentLabelMutation,
@@ -10,7 +16,10 @@ import {
import {useTheme, atoms as a} from '#/alf'
import {Text} from '#/components/Typography'
import {PreferenceButton} from './PreferenceButton'
import * as Dialog from '#/components/Dialog'
import {Button, ButtonText, ButtonIcon} from '#/components/Button'
import {ArrowTriangleBottom_Stroke2_Corner1_Rounded as ArrowTriangleBottom} from '../icons/ArrowTriangle'
import {Check_Stroke2_Corner0_Rounded as Check} from '../icons/Check'
export function ModerationLabelPref({
labelValueDefinition,
@@ -21,12 +30,35 @@ export function ModerationLabelPref({
labelerDid: string | undefined
disabled?: boolean
}) {
const {_} = useLingui()
const t = useTheme()
const allLabelStrings = useLabelStrings()
const {data: preferences} = usePreferencesQuery()
const {mutate, variables} = usePreferencesSetContentLabelMutation()
const control = Dialog.useDialogControl()
const {identifier} = labelValueDefinition
const {data: preferences} = usePreferencesQuery()
const {mutate, variables} = usePreferencesSetContentLabelMutation()
const savedPref = labelerDid
? preferences?.moderationPrefs.mods.find(m => m.did === labelerDid)?.labels[
identifier
]
: preferences?.moderationPrefs.labels[identifier]
const pref = variables?.visibility ?? savedPref ?? 'warn'
const settingDesc = useLabelBehaviorDescription(labelValueDefinition, pref)
const hideLabel = useLabelLongBehaviorDescription(
labelValueDefinition,
'hide',
)
const warnLabel = useLabelLongBehaviorDescription(
labelValueDefinition,
'warn',
)
const ignoreLabel = useLabelLongBehaviorDescription(
labelValueDefinition,
'ignore',
)
const allLabelStrings = useLabelStrings()
const labelStrings = labelValueDefinition.locales[0] // TODO look up locale
? labelValueDefinition.locales[0]
: labelValueDefinition.identifier in allLabelStrings
@@ -36,34 +68,141 @@ export function ModerationLabelPref({
description: `Labeled "${labelValueDefinition.identifier}"`,
}
const savedPref = labelerDid
? preferences?.moderationPrefs.mods.find(m => m.did === labelerDid)?.labels[
identifier
]
: preferences?.moderationPrefs.labels[identifier]
const pref = variables?.visibility ?? savedPref ?? 'warn'
const canWarn = !(
labelValueDefinition.blurs === 'none' &&
labelValueDefinition.severity === 'none'
)
const onSelectPref = (newPref: LabelPreference) =>
mutate({label: identifier, visibility: newPref, labelerDid})
return (
<View style={[a.flex_row, a.justify_between, a.gap_lg, a.align_center]}>
<View style={[a.gap_xs, a.flex_1]}>
<>
<Pressable
onPress={() => control.open()}
accessibilityLabel={settingDesc}
accessibilityHint=""
style={[
a.flex_row,
a.justify_between,
a.gap_sm,
a.align_center,
t.atoms.bg_contrast_50,
a.px_lg,
a.py_lg,
a.rounded_sm,
]}>
<Text style={[a.font_bold]}>{labelStrings.name}</Text>
<Text style={[t.atoms.text_contrast_medium, a.leading_snug]}>
<Text
style={[t.atoms.text_contrast_medium, a.flex_1]}
numberOfLines={1}>
{labelStrings.description}
</Text>
</View>
<View style={[{width: 110}]}>
{!disabled && (
<PreferenceButton
name={labelStrings.name}
pref={pref}
labelValueDefinition={labelValueDefinition}
onSelectPref={onSelectPref}
/>
<View
style={[
{width: 100},
a.flex_row,
a.align_center,
a.justify_end,
a.gap_xs,
a.rounded_2xs,
]}>
{!disabled && (
<Text style={[t.atoms.text_contrast_medium, a.font_semibold]}>
{settingDesc}
</Text>
)}
<ArrowTriangleBottom
width={8}
fill={t.atoms.text_contrast_medium.color}
/>
</View>
)}
</View>
</View>
</Pressable>
<Dialog.Outer control={control}>
<Dialog.Handle />
<Dialog.Inner
label={_(msg`Settings: ${labelValueDefinition.identifier}`)}
style={[a.gap_sm]}>
<Text style={[a.text_2xl, a.font_bold, a.pb_xs, t.atoms.text]}>
{labelStrings.name}
</Text>
<Text style={[a.text_md, a.pb_sm, t.atoms.text_contrast_medium]}>
{labelStrings.description}
</Text>
{disabled ? (
<Button
label={_(msg`Close`)}
size="large"
variant="solid"
color="secondary"
onPress={() => control.close()}>
<ButtonText style={[a.flex_1, a.text_left]}>
<Trans>Close</Trans>
</ButtonText>
</Button>
) : (
<>
<Button
label={hideLabel}
size="large"
variant="solid"
color={pref === 'hide' ? 'primary' : 'secondary'}
onPress={() => {
onSelectPref('hide')
control.close()
}}>
<ButtonText style={[a.flex_1, a.text_left]}>
{hideLabel}
</ButtonText>
{pref === 'hide' && (
<ButtonIcon icon={Check} position="right" />
)}
</Button>
{canWarn && (
<Button
label={warnLabel}
size="large"
variant="solid"
color={pref === 'warn' ? 'primary' : 'secondary'}
onPress={() => {
onSelectPref('warn')
control.close()
}}>
<ButtonText style={[a.flex_1, a.text_left]}>
{warnLabel}
</ButtonText>
{pref === 'warn' && (
<ButtonIcon icon={Check} position="right" />
)}
</Button>
)}
<Button
label={ignoreLabel}
size="large"
variant="solid"
color={!disabled && pref === 'ignore' ? 'primary' : 'secondary'}
onPress={() => {
onSelectPref('ignore')
control.close()
}}>
<ButtonText style={[a.flex_1, a.text_left]}>
{ignoreLabel}
</ButtonText>
{pref === 'ignore' && (
<ButtonIcon icon={Check} position="right" />
)}
</Button>
</>
)}
</Dialog.Inner>
</Dialog.Outer>
</>
)
}
+8 -15
View File
@@ -141,8 +141,6 @@ export function ProfileLabelsSectionInner({
) as InterprettedLabelValueDefinition[]
}, [labelerInfo, labelValues])
console.log(headerHeight, scrollElRef.current)
return (
<ScrollView
ref={scrollElRef}
@@ -195,20 +193,15 @@ export function ProfileLabelsSectionInner({
) : null}
</View>
{labelDefs.length > 0 && (
<View
style={[a.mt_xl, t.atoms.bg_contrast_25, a.rounded_md, a.py_xs]}>
{labelDefs.map((labelDef, i) => {
<View style={[a.mt_xl, a.gap_sm]}>
{labelDefs.map(labelDef => {
return (
<React.Fragment key={labelDef.identifier}>
{i !== 0 && <Divider />}
<View style={[a.py_md, a.px_md]}>
<ModerationLabelPref
disabled={isSubscribed ? undefined : true}
labelValueDefinition={labelDef}
labelerDid={labelerInfo.creator.did}
/>
</View>
</React.Fragment>
<ModerationLabelPref
key={labelDef.identifier}
disabled={isSubscribed ? undefined : true}
labelValueDefinition={labelDef}
labelerDid={labelerInfo.creator.did}
/>
)
})}
</View>