Move moderation card from the profile header to a tab

This commit is contained in:
Paul Frazee
2024-02-15 20:04:07 -08:00
parent bfe7c2ff38
commit 58af8e0f99
3 changed files with 112 additions and 35 deletions
+19 -2
View File
@@ -5,9 +5,10 @@ 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 {RaisingHande4Finger_Stroke2_Corner0_Rounded as RaisingHand} from '#/components/icons/RaisingHand'
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,
@@ -64,10 +65,12 @@ export function Content({
title,
description,
handle,
likeCount,
}: {
title: string
description?: string
handle: string
likeCount?: number
}) {
const t = useTheme()
@@ -85,9 +88,23 @@ export function Content({
</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>
<RaisingHand size="xl" style={[a.z_10]} fill={t.palette.primary_500} />
<ChevronRight size="md" style={[a.z_10]} fill={t.palette.contrast_500} />
</View>
)
}
-30
View File
@@ -56,14 +56,10 @@ import {Shadow} from '#/state/cache/types'
import {useRequireAuth} from '#/state/session'
import {LabelsOnMe} from '../util/moderation/LabelsOnMe'
import {useProfileShadow} from 'state/cache/profile-shadow'
import * as ModerationServiceCard from '#/components/ModerationServiceCard'
import {getModerationServiceTitle} from '#/lib/moderation'
import {useOpenGlobalDialog} from '#/components/dialogs'
import {ReportDialog} from '#/components/dialogs/ReportDialog'
import {NEW_REPORT_DIALOG_ENABLED} from '#/lib/build-flags'
import {useTheme} from '#/alf'
let ProfileHeaderLoading = (_props: {}): React.ReactNode => {
const pal = usePalette('default')
return (
@@ -99,7 +95,6 @@ let ProfileHeader = ({
hideBackButton = false,
isPlaceholderProfile,
}: Props): React.ReactNode => {
const t = useTheme()
const profile: Shadow<AppBskyActorDefs.ProfileViewDetailed> =
useProfileShadow(profileUnshadowed)
const pal = usePalette('default')
@@ -637,31 +632,6 @@ let ProfileHeader = ({
{isMe && (
<LabelsOnMe details={{did: profile.did}} labels={profile.labels} />
)}
<ModerationServiceCard.Loader
did={profile.did}
component={({modservice}) => (
<ModerationServiceCard.Link modservice={modservice}>
{ctx => (
<ModerationServiceCard.Card.Outer
style={[
...(ctx.focused || ctx.hovered
? [t.atoms.bg_contrast_50]
: []),
]}>
<ModerationServiceCard.Card.Content
title={getModerationServiceTitle({
displayName: modservice.creator.displayName,
handle: modservice.creator.handle,
})}
description={modservice.description}
handle={modservice.creator.handle}
/>
</ModerationServiceCard.Card.Outer>
)}
</ModerationServiceCard.Link>
)}
/>
</View>
{showSuggestedFollows && (
+93 -3
View File
@@ -10,7 +10,7 @@ import {
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
import {CenteredView} from '../com/util/Views'
import {CenteredView, ScrollView} from '../com/util/Views'
import {ListRef} from '../com/util/List'
import {ScreenHider} from 'view/com/util/moderation/ScreenHider'
import {Feed} from 'view/com/posts/Feed'
@@ -49,6 +49,10 @@ import {usePalette} from 'lib/hooks/usePalette'
import {isNative} from '#/platform/detection'
import {isInvalidHandle} from '#/lib/strings/handles'
import {useTheme, atoms as a} from '#/alf'
import * as ModerationServiceCard from '#/components/ModerationServiceCard'
import {RaisingHande4Finger_Stroke2_Corner0_Rounded as RaisingHand} from '#/components/icons/RaisingHand'
interface SectionRef {
scrollToTop: () => void
}
@@ -158,6 +162,7 @@ function ProfileScreenLoaded({
const likesSectionRef = React.useRef<SectionRef>(null)
const feedsSectionRef = React.useRef<SectionRef>(null)
const listsSectionRef = React.useRef<SectionRef>(null)
const moderationSectionRef = React.useRef<SectionRef>(null)
useSetTitle(combinedDisplayName(profile))
@@ -175,6 +180,9 @@ function ProfileScreenLoaded({
const showLikesTab = isMe
const showFeedsTab = hasSession && (isMe || extraInfoQuery.data?.hasFeedgens)
const showListsTab = hasSession && (isMe || extraInfoQuery.data?.hasLists)
const showModerationTab =
hasSession && (isMe || true) /* TODO true should be associated modservice */
const sectionTitles = useMemo<string[]>(() => {
return [
_(msg`Posts`),
@@ -183,8 +191,16 @@ function ProfileScreenLoaded({
showLikesTab ? _(msg`Likes`) : undefined,
showFeedsTab ? _(msg`Feeds`) : undefined,
showListsTab ? _(msg`Lists`) : undefined,
showModerationTab ? _(msg`Moderation`) : undefined,
].filter(Boolean) as string[]
}, [showRepliesTab, showLikesTab, showFeedsTab, showListsTab, _])
}, [
showRepliesTab,
showLikesTab,
showFeedsTab,
showListsTab,
showModerationTab,
_,
])
let nextIndex = 0
const postsIndex = nextIndex++
@@ -205,6 +221,10 @@ function ProfileScreenLoaded({
if (showListsTab) {
listsIndex = nextIndex++
}
let moderationIndex: number | null = null
if (showModerationTab) {
moderationIndex = nextIndex++
}
const scrollSectionToTop = React.useCallback(
(index: number) => {
@@ -220,9 +240,19 @@ function ProfileScreenLoaded({
feedsSectionRef.current?.scrollToTop()
} else if (index === listsIndex) {
listsSectionRef.current?.scrollToTop()
} else if (index === moderationIndex) {
moderationSectionRef.current?.scrollToTop()
}
},
[postsIndex, repliesIndex, mediaIndex, likesIndex, feedsIndex, listsIndex],
[
postsIndex,
repliesIndex,
mediaIndex,
likesIndex,
feedsIndex,
listsIndex,
moderationIndex,
],
)
useFocusEffect(
@@ -372,6 +402,17 @@ function ProfileScreenLoaded({
/>
)
: null}
{showModerationTab
? ({headerHeight, isFocused, scrollElRef}) => (
<ModerationSection
// ref={moderationSectionRef}
did={profile.did}
scrollElRef={scrollElRef as ListRef}
headerOffset={headerHeight}
enabled={isFocused}
/>
)
: null}
</PagerWithHeader>
{hasSession && (
<FAB
@@ -458,6 +499,55 @@ function ProfileEndOfFeed() {
)
}
function ModerationSection({did}: {did: string}) {
const t = useTheme()
return (
<ScrollView>
<ModerationServiceCard.Loader
did={did}
component={({modservice}) => (
<ModerationServiceCard.Link modservice={modservice}>
{ctx => (
<View
style={[
a.flex_1,
a.flex_row,
a.align_center,
a.gap_md,
a.p_md,
a.border_t,
t.atoms.border_contrast_low,
...(ctx.focused || ctx.hovered
? [t.atoms.bg_contrast_25]
: []),
]}>
<View
style={[
{backgroundColor: t.palette.negative_25},
a.p_lg,
a.rounded_sm,
]}>
<RaisingHand
width={36}
style={[a.z_10]}
fill={t.palette.negative_500}
/>
</View>
<ModerationServiceCard.Card.Content
title="Moderation service"
description={modservice.description}
handle={modservice.creator.handle}
likeCount={modservice.likeCount}
/>
</View>
)}
</ModerationServiceCard.Link>
)}
/>
</ScrollView>
)
}
function useRichText(text: string): [RichTextAPI, boolean] {
const [prevText, setPrevText] = React.useState(text)
const [rawRT, setRawRT] = React.useState(() => new RichTextAPI({text}))