Rework label pref ui
This commit is contained in:
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user