Improve Moderation screen and dialog

This commit is contained in:
Eric Bailey
2024-02-15 17:01:00 -06:00
parent c5d31cd8b0
commit 8870c5a183
3 changed files with 106 additions and 41 deletions
+10 -1
View File
@@ -171,11 +171,18 @@ export type LinkProps = Omit<BaseLinkProps, 'warnOnMismatchingTextChild'> &
* Intended to behave as a web anchor tag. For more complex routing, use a * Intended to behave as a web anchor tag. For more complex routing, use a
* `Button`. * `Button`.
*/ */
export function Link({children, to, action = 'push', ...rest}: LinkProps) { export function Link({
children,
to,
action = 'push',
onPress: outerOnPress,
...rest
}: LinkProps) {
const {href, isExternal, onPress} = useLink({ const {href, isExternal, onPress} = useLink({
to, to,
displayText: typeof children === 'string' ? children : '', displayText: typeof children === 'string' ? children : '',
action, action,
onPress: outerOnPress,
}) })
return ( return (
@@ -218,6 +225,7 @@ export function InlineLink({
action = 'push', action = 'push',
warnOnMismatchingTextChild, warnOnMismatchingTextChild,
style, style,
onPress: outerOnPress,
...rest ...rest
}: InlineLinkProps) { }: InlineLinkProps) {
const t = useTheme() const t = useTheme()
@@ -227,6 +235,7 @@ export function InlineLink({
displayText: stringChildren ? children : '', displayText: stringChildren ? children : '',
action, action,
warnOnMismatchingTextChild, warnOnMismatchingTextChild,
onPress: outerOnPress,
}) })
const {state: focused, onIn: onFocus, onOut: onBlur} = useInteractionState() const {state: focused, onIn: onFocus, onOut: onBlur} = useInteractionState()
const { const {
+20 -1
View File
@@ -7,6 +7,7 @@ import {Text} from '#/components/Typography'
import {RichText} from '#/components/RichText' import {RichText} from '#/components/RichText'
import {RaisingHande4Finger_Stroke2_Corner0_Rounded as RaisingHand} from '#/components/icons/RaisingHand' import {RaisingHande4Finger_Stroke2_Corner0_Rounded as RaisingHand} from '#/components/icons/RaisingHand'
import {UserAvatar} from '#/view/com/util/UserAvatar' import {UserAvatar} from '#/view/com/util/UserAvatar'
import {sanitizeHandle} from '#/lib/strings/handles'
export function Outer({ export function Outer({
children, children,
@@ -43,6 +44,22 @@ export function Avatar({avatar}: {avatar?: string}) {
return <UserAvatar type="list" size={40} avatar={avatar} /> return <UserAvatar type="list" size={40} avatar={avatar} />
} }
export function Title({value}: {value: string}) {
return <Text style={[a.text_md, a.font_bold, a.pb_2xs]}>{value}</Text>
}
export function Description({value, handle}: {value?: string; handle: string}) {
return value ? (
<RichText value={value} style={[]} />
) : (
<Text>
<Trans>
Moderation service managed by @{sanitizeHandle(handle, '@')}
</Trans>
</Text>
)
}
export function Content({ export function Content({
title, title,
description, description,
@@ -63,7 +80,9 @@ export function Content({
<RichText value={description} style={[]} /> <RichText value={description} style={[]} />
) : ( ) : (
<Text> <Text>
<Trans>Moderation service managed by @{handle}</Trans> <Trans>
Moderation service managed by @{sanitizeHandle(handle, '@')}
</Trans>
</Text> </Text>
)} )}
</View> </View>
+76 -39
View File
@@ -13,6 +13,10 @@ import * as ModerationServiceCard from '#/components/ModerationServiceCard'
import {getModerationServiceTitle} from '#/lib/moderation' import {getModerationServiceTitle} from '#/lib/moderation'
import {UsePreferencesQueryResponse} from '#/state/queries/preferences' import {UsePreferencesQueryResponse} from '#/state/queries/preferences'
import {useModServiceLabelGroupEnableMutation} from '#/state/queries/modservice' import {useModServiceLabelGroupEnableMutation} from '#/state/queries/modservice'
import {Divider} from '#/components/Divider'
import {InlineLink} from '#/components/Link'
import {useDialogStateControlContext} from '#/state/dialogs'
import {logger} from '#/logger'
function LabelerToggle({ function LabelerToggle({
labelGroup, labelGroup,
@@ -24,14 +28,24 @@ function LabelerToggle({
preferences: UsePreferencesQueryResponse preferences: UsePreferencesQueryResponse
}) { }) {
const t = useTheme() const t = useTheme()
const {mutateAsync, variables} = useModServiceLabelGroupEnableMutation() const {
mutateAsync: toggleGroupEnabled,
variables: optimisticToggleGroupEnabled,
} = useModServiceLabelGroupEnableMutation()
const {closeAllDialogs} = useDialogStateControlContext()
const modservicePreferences = preferences.moderationOpts.mods.find( const labelerPrefs = React.useMemo(
({did}) => did === labeler.creator.did, () =>
preferences.moderationOpts.mods.find(
({did}) => did === labeler.creator.did,
),
[preferences.moderationOpts.mods, labeler.creator.did],
) )
const enabled = const isLabelerEnabled = !!labelerPrefs?.enabled
variables?.enabled ?? const isEnabled = isLabelerEnabled
!modservicePreferences?.disabledLabelGroups?.includes(labelGroup) ? optimisticToggleGroupEnabled?.enabled ??
!labelerPrefs?.disabledLabelGroups?.includes(labelGroup)
: false
const title = getModerationServiceTitle({ const title = getModerationServiceTitle({
displayName: labeler.creator.displayName, displayName: labeler.creator.displayName,
handle: labeler.creator.handle, handle: labeler.creator.handle,
@@ -39,42 +53,66 @@ function LabelerToggle({
const onToggleEnabled = React.useCallback(async () => { const onToggleEnabled = React.useCallback(async () => {
try { try {
await mutateAsync({ if (!labelerPrefs) throw new Error(`labelerPrefs not found`)
// @ts-ignore TODO
did: modservicePreferences?.did, await toggleGroupEnabled({
did: labelerPrefs.did,
group: labelGroup, group: labelGroup,
enabled: !enabled, enabled: !isEnabled,
}) })
} catch (e: any) { } catch (e: any) {
// TODO logger.error(`Failed to toggle label group enabled`, {
console.error(e) message: e.message,
labelGroup,
})
} }
}, [mutateAsync, enabled, modservicePreferences, labelGroup]) }, [toggleGroupEnabled, isEnabled, labelerPrefs, labelGroup])
return ( return (
<Toggle.Item <View style={[a.rounded_sm, a.border, t.atoms.border_contrast_low]}>
name={labeler.creator.did} <View style={[a.flex_row, a.justify_between, a.p_md]}>
label={title} <View style={[a.flex_row, a.align_center, a.gap_md]}>
key={labeler.creator.did}
value={enabled}
onChange={onToggleEnabled}>
{ctx => (
<ModerationServiceCard.Card.Outer
style={[
...(ctx.focused || ctx.hovered ? [t.atoms.bg_contrast_50] : []),
ctx.selected && {
backgroundColor: t.palette.primary_50,
borderColor: t.palette.primary_100,
},
]}>
<ModerationServiceCard.Card.Avatar avatar={labeler.creator.avatar} /> <ModerationServiceCard.Card.Avatar avatar={labeler.creator.avatar} />
<ModerationServiceCard.Card.Content <View>
title={title} <ModerationServiceCard.Card.Title value={title} />
description={labeler.description} <ModerationServiceCard.Card.Description
handle={labeler.creator.handle} value={labeler.description}
/> handle={labeler.creator.handle}
</ModerationServiceCard.Card.Outer> />
)} </View>
</Toggle.Item> </View>
<Toggle.Item
disabled={!isLabelerEnabled}
name={labeler.creator.did}
label={title}
key={labeler.creator.did}
value={isEnabled}
onChange={onToggleEnabled}>
<Toggle.Label>{isEnabled ? 'Enabled' : 'Disabled'}</Toggle.Label>
<Toggle.Switch />
</Toggle.Item>
</View>
<Divider />
<View style={[a.flex_row, a.p_md]}>
<Text style={[a.text_xs]}>
Configure more settings for this labeler{' '}
<InlineLink
to={{
screen: 'ProfileModservice',
params: {
name: labeler.creator.handle,
},
}}
style={[a.text_xs]}
onPress={closeAllDialogs}>
here.
</InlineLink>
</Text>
</View>
</View>
) )
} }
@@ -115,11 +153,10 @@ export function SettingsDialog({
a.p_md, a.p_md,
a.rounded_md, a.rounded_md,
a.mb_xl, a.mb_xl,
a.border, t.atoms.bg_contrast_25,
t.atoms.border_contrast_low,
]}> ]}>
<View style={[a.flex_1, a.gap_xs]}> <View style={[a.flex_1, a.gap_xs]}>
<Text style={[a.text_md, a.font_bold]}>{groupInfoStrings.name}</Text> <Text style={[a.font_bold]}>{groupInfoStrings.name}</Text>
<Text style={[a.leading_tight, {maxWidth: 400}]}> <Text style={[a.leading_tight, {maxWidth: 400}]}>
{groupInfoStrings.description} {groupInfoStrings.description}
</Text> </Text>