Move moderation card from the profile header to a tab
This commit is contained in:
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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 && (
|
||||
|
||||
@@ -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}))
|
||||
|
||||
Reference in New Issue
Block a user