Clean up ModServiceCard, rename to LabelingServiceCard

This commit is contained in:
Eric Bailey
2024-03-13 14:24:06 -05:00
parent 444dbe928a
commit 3679852e17
7 changed files with 208 additions and 197 deletions
+1 -1
View File
@@ -391,7 +391,7 @@ export function Button({
]}>
<LinearGradient
colors={
state.hovered || state.pressed || state.focused
state.hovered || state.pressed
? gradientHoverColors
: gradientColors
}
@@ -0,0 +1,184 @@
import React from 'react'
import {View} from 'react-native'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {AppBskyLabelerDefs} from '@atproto/api'
import {getLabelingServiceTitle} from '#/lib/moderation'
import {Link as InternalLink, LinkProps} from '#/components/Link'
import {Text} from '#/components/Typography'
import {useLabelerInfoQuery} from '#/state/queries/labeler'
import {atoms as a, useTheme, ViewStyleProp, useBreakpoints} from '#/alf'
import {RichText} from '#/components/RichText'
import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '../icons/Chevron'
import {UserAvatar} from '#/view/com/util/UserAvatar'
import {sanitizeHandle} from '#/lib/strings/handles'
import {pluralize} from '#/lib/strings/helpers'
type LabelingServiceProps = {
labeler: AppBskyLabelerDefs.LabelerViewDetailed
}
export function Outer({
children,
style,
}: React.PropsWithChildren<ViewStyleProp>) {
return (
<View
style={[
a.flex_row,
a.align_center,
a.gap_sm,
a.w_full,
a.p_lg,
a.pr_md,
a.overflow_hidden,
style,
]}>
<View style={[a.flex_row, a.align_center, a.w_full, a.gap_md]}>
{children}
</View>
</View>
)
}
export function Avatar({avatar}: {avatar?: string}) {
return <UserAvatar type="labeler" size={40} avatar={avatar} />
}
export function Title({value}: {value: string}) {
return <Text style={[a.text_md, a.font_bold]}>{value}</Text>
}
export function Description({value, handle}: {value?: string; handle: string}) {
return value ? (
<RichText value={value} style={[]} />
) : (
<Text>
<Trans>By {sanitizeHandle(handle, '@')}</Trans>
</Text>
)
}
export function LikeCount({count}: {count: number}) {
const t = useTheme()
return (
<Text
style={[
a.mt_sm,
a.text_sm,
t.atoms.text_contrast_medium,
{fontWeight: '500'},
]}>
<Trans>
Liked by {count} {pluralize(count, 'user')}
</Trans>
</Text>
)
}
export function Content({children}: React.PropsWithChildren<{}>) {
const t = useTheme()
const {gtMobile} = useBreakpoints()
return (
<View
style={[
a.flex_1,
a.flex_row,
a.gap_md,
a.align_center,
a.justify_between,
]}>
<View style={[a.gap_xs, a.flex_1, gtMobile && a.gap_sm]}>{children}</View>
<ChevronRight size="md" style={[a.z_10, t.atoms.text_contrast_low]} />
</View>
)
}
/**
* The canonical view for a moderation service. Use this or compose your own.
*/
export function Default({
labeler,
style,
}: LabelingServiceProps & ViewStyleProp) {
return (
<Outer style={style}>
<Avatar />
<Content>
<Title
value={getLabelingServiceTitle({
displayName: labeler.creator.displayName,
handle: labeler.creator.handle,
})}
/>
<Description
value={labeler.creator.description}
handle={labeler.creator.handle}
/>
{labeler.likeCount ? <LikeCount count={labeler.likeCount} /> : null}
</Content>
</Outer>
)
}
export function Link({
children,
labeler,
}: LabelingServiceProps & Pick<LinkProps, 'children'>) {
const {_} = useLingui()
return (
<InternalLink
to={{
screen: 'Profile',
params: {
name: labeler.creator.handle,
},
}}
label={_(
msg`View the moderation service provided by @${labeler.creator.handle}`,
)}>
{children}
</InternalLink>
)
}
// TODO not finished yet
export function DefaultSkeleton() {
return (
<View>
<Text>Loading</Text>
</View>
)
}
export function Loader({
did,
loading: LoadingComponent = DefaultSkeleton,
error: ErrorComponent,
component: Component,
}: {
did: string
loading?: React.ComponentType<{}>
error?: React.ComponentType<{error: string}>
component: React.ComponentType<{
labeler: AppBskyLabelerDefs.LabelerViewDetailed
}>
}) {
const {isLoading, data, error} = useLabelerInfoQuery({did})
return isLoading ? (
LoadingComponent ? (
<LoadingComponent />
) : null
) : error || !data ? (
ErrorComponent ? (
<ErrorComponent error={error?.message || 'Unknown error'} />
) : null
) : (
<Component labeler={data} />
)
}
@@ -1,104 +0,0 @@
import React from 'react'
import {View} from 'react-native'
import {Trans} from '@lingui/macro'
import {atoms as a, useTheme, ViewStyleProp, flatten} from '#/alf'
import {Text} from '#/components/Typography'
import {RichText} from '#/components/RichText'
import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '../icons/Chevron'
import {UserAvatar} from '#/view/com/util/UserAvatar'
import {sanitizeHandle} from '#/lib/strings/handles'
import {pluralize} from '#/lib/strings/helpers'
export function Outer({
children,
style,
}: React.PropsWithChildren<ViewStyleProp>) {
return (
<View
style={[
a.flex_row,
a.align_center,
a.gap_sm,
a.w_full,
a.pt_md,
a.pb_md,
a.pl_md,
a.pr_lg,
a.overflow_hidden,
flatten(style),
]}>
<View style={[a.flex_row, a.align_center, a.w_full, a.gap_md]}>
{children}
</View>
</View>
)
}
export function Avatar({avatar}: {avatar?: string}) {
return <UserAvatar type="labeler" 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({
title,
description,
handle,
likeCount,
}: {
title: string
description?: string
handle: string
likeCount?: number
}) {
const t = useTheme()
return (
<View style={[a.flex_1, a.flex_row, a.align_center, a.justify_between]}>
<View>
<Text style={[a.text_md, a.font_bold, a.pb_2xs]}>{title}</Text>
{description ? (
<RichText value={description} style={[]} />
) : (
<Text>
<Trans>
Moderation service managed by {sanitizeHandle(handle, '@')}
</Trans>
</Text>
)}
{typeof likeCount === 'number' && (
<Text
style={[
t.atoms.text_contrast_medium,
a.text_sm,
{fontWeight: '500'},
a.mt_sm,
]}>
<Trans>
Liked by {likeCount || 0} {pluralize(likeCount || 0, 'user')}
</Trans>
</Text>
)}
</View>
<ChevronRight size="md" style={[a.z_10, t.atoms.text_contrast_low]} />
</View>
)
}
@@ -1,73 +0,0 @@
import React from 'react'
import {View} from 'react-native'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {AppBskyLabelerDefs} from '@atproto/api'
import {Link as InternalLink, LinkProps} from '#/components/Link'
import {Text} from '#/components/Typography'
import {useLabelerInfoQuery} from '#/state/queries/labeler'
export * as Card from '#/components/ModerationServiceCard/Card'
type ModerationServiceProps = {
labeler: AppBskyLabelerDefs.LabelerViewDetailed
}
export function Link({
children,
labeler,
}: ModerationServiceProps & Pick<LinkProps, 'children'>) {
const {_} = useLingui()
return (
<InternalLink
to={{
screen: 'Profile',
params: {
name: labeler.creator.handle,
},
}}
label={_(
msg`View the moderation service provided by @${labeler.creator.handle}`,
)}>
{children}
</InternalLink>
)
}
export function ModerationServiceCardSkeleton() {
return (
<View>
<Text>Loading</Text>
</View>
)
}
export function Loader({
did,
loading: LoadingComponent = ModerationServiceCardSkeleton,
error: ErrorComponent,
component: Component,
}: {
did: string
loading?: React.ComponentType<{}>
error?: React.ComponentType<{error: string}>
component: React.ComponentType<{
labeler: AppBskyLabelerDefs.LabelerViewDetailed
}>
}) {
const {isLoading, data, error} = useLabelerInfoQuery({did})
return isLoading ? (
LoadingComponent ? (
<LoadingComponent />
) : null
) : error || !data ? (
ErrorComponent ? (
<ErrorComponent error={error?.message || 'Unknown error'} />
) : null
) : (
<Component labeler={data} />
)
}
+2 -2
View File
@@ -4,7 +4,7 @@ import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {AppBskyLabelerDefs} from '@atproto/api'
import {getModerationServiceTitle} from '#/lib/moderation'
import {getLabelingServiceTitle} from '#/lib/moderation'
import {ReportOption} from '#/lib/moderation/useReportOptions'
import {atoms as a, useTheme, tokens, native} from '#/alf'
@@ -140,7 +140,7 @@ export function SubmitView({
onChange={setSelectedServices}>
<View style={[a.flex_row, a.gap_sm, a.flex_wrap]}>
{labelers.map(labeler => {
const title = getModerationServiceTitle({
const title = getLabelingServiceTitle({
displayName: labeler.creator.displayName,
handle: labeler.creator.handle,
})
+1 -1
View File
@@ -28,7 +28,7 @@ export function isJustAMute(modui: ModerationUI): boolean {
return modui.filters.length === 1 && modui.filters[0].type === 'muted'
}
export function getModerationServiceTitle({
export function getLabelingServiceTitle({
displayName,
handle,
}: {
+20 -16
View File
@@ -39,8 +39,7 @@ import * as Toggle from '#/components/forms/Toggle'
import {InlineLink, Link} from '#/components/Link'
import {Button, ButtonText} from '#/components/Button'
import {Loader} from '#/components/Loader'
import {getModerationServiceTitle} from '#/lib/moderation'
import * as ModerationServiceCard from '#/components/ModerationServiceCard'
import * as LabelingService from '#/components/LabelingServiceCard'
import {GlobalModerationLabelPref} from '#/components/moderation/GlobalModerationLabelPref'
import {useGlobalDialogsControlContext} from '#/components/dialogs/Context'
import {useModalControls} from '#/state/modals'
@@ -396,21 +395,26 @@ export function ModerationScreenInner({
return (
<React.Fragment key={mod.creator.did}>
{i !== 0 && <Divider />}
<ModerationServiceCard.Link labeler={mod}>
<ModerationServiceCard.Card.Outer>
<ModerationServiceCard.Card.Avatar
avatar={mod.creator.avatar}
<LabelingService.Link labeler={mod}>
{state => (
<LabelingService.Default
labeler={mod}
style={[
i === 0 && {
borderTopLeftRadius: a.rounded_sm.borderRadius,
borderTopRightRadius: a.rounded_sm.borderRadius,
},
i === labelers.length - 1 && {
borderBottomLeftRadius: a.rounded_sm.borderRadius,
borderBottomRightRadius: a.rounded_sm.borderRadius,
},
(state.hovered || state.pressed) && [
t.atoms.bg_contrast_50,
],
]}
/>
<ModerationServiceCard.Card.Content
title={getModerationServiceTitle({
displayName: mod.creator.displayName,
handle: mod.creator.handle,
})}
handle={mod.creator.handle}
description={mod.creator.description || ''}
/>
</ModerationServiceCard.Card.Outer>
</ModerationServiceCard.Link>
)}
</LabelingService.Link>
</React.Fragment>
)
})}