From 3679852e17c81c1010c6616d43a0300f607ed99f Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Wed, 13 Mar 2024 14:24:06 -0500 Subject: [PATCH] Clean up ModServiceCard, rename to LabelingServiceCard --- src/components/Button.tsx | 2 +- src/components/LabelingServiceCard/index.tsx | 184 ++++++++++++++++++ src/components/ModerationServiceCard/Card.tsx | 104 ---------- .../ModerationServiceCard/index.tsx | 73 ------- src/components/ReportDialog/SubmitView.tsx | 4 +- src/lib/moderation.ts | 2 +- src/screens/Moderation/index.tsx | 36 ++-- 7 files changed, 208 insertions(+), 197 deletions(-) create mode 100644 src/components/LabelingServiceCard/index.tsx delete mode 100644 src/components/ModerationServiceCard/Card.tsx delete mode 100644 src/components/ModerationServiceCard/index.tsx diff --git a/src/components/Button.tsx b/src/components/Button.tsx index 702ec0619f..0e22944a33 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -391,7 +391,7 @@ export function Button({ ]}> ) { + return ( + + + {children} + + + ) +} + +export function Avatar({avatar}: {avatar?: string}) { + return +} + +export function Title({value}: {value: string}) { + return {value} +} + +export function Description({value, handle}: {value?: string; handle: string}) { + return value ? ( + + ) : ( + + By {sanitizeHandle(handle, '@')} + + ) +} + +export function LikeCount({count}: {count: number}) { + const t = useTheme() + return ( + + + Liked by {count} {pluralize(count, 'user')} + + + ) +} + +export function Content({children}: React.PropsWithChildren<{}>) { + const t = useTheme() + const {gtMobile} = useBreakpoints() + + return ( + + {children} + + + + ) +} + +/** + * The canonical view for a moderation service. Use this or compose your own. + */ +export function Default({ + labeler, + style, +}: LabelingServiceProps & ViewStyleProp) { + return ( + + + + + <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} /> + ) +} diff --git a/src/components/ModerationServiceCard/Card.tsx b/src/components/ModerationServiceCard/Card.tsx deleted file mode 100644 index 599ad138f0..0000000000 --- a/src/components/ModerationServiceCard/Card.tsx +++ /dev/null @@ -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> - ) -} diff --git a/src/components/ModerationServiceCard/index.tsx b/src/components/ModerationServiceCard/index.tsx deleted file mode 100644 index e0c07732ca..0000000000 --- a/src/components/ModerationServiceCard/index.tsx +++ /dev/null @@ -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} /> - ) -} diff --git a/src/components/ReportDialog/SubmitView.tsx b/src/components/ReportDialog/SubmitView.tsx index 60453eb76e..167d10ddf8 100644 --- a/src/components/ReportDialog/SubmitView.tsx +++ b/src/components/ReportDialog/SubmitView.tsx @@ -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, }) diff --git a/src/lib/moderation.ts b/src/lib/moderation.ts index 64efe62f64..4105c2c2dd 100644 --- a/src/lib/moderation.ts +++ b/src/lib/moderation.ts @@ -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, }: { diff --git a/src/screens/Moderation/index.tsx b/src/screens/Moderation/index.tsx index 5674b2ebea..09e4191957 100644 --- a/src/screens/Moderation/index.tsx +++ b/src/screens/Moderation/index.tsx @@ -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> ) })}