Improve Moderation screen and dialog
This commit is contained in:
+10
-1
@@ -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 {
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user