Moderator variation of profile
This commit is contained in:
@@ -152,75 +152,87 @@ export function ProfileHeaderDropdownBtn({
|
||||
[currentAccount, profile],
|
||||
)
|
||||
const dropdownItems: DropdownItem[] = React.useMemo(() => {
|
||||
let items: DropdownItem[] = [
|
||||
{
|
||||
testID: 'profileHeaderDropdownShareBtn',
|
||||
label: isWeb ? _(msg`Copy link to profile`) : _(msg`Share`),
|
||||
onPress: onPressShare,
|
||||
let items: DropdownItem[] = []
|
||||
|
||||
if (hasSession && profile.associated?.modservice) {
|
||||
items.push({
|
||||
testID: 'profileHeaderDropdownFollowBtn',
|
||||
label: _(msg`Follow Account`),
|
||||
onPress: () => {}, // TODO
|
||||
icon: {
|
||||
ios: {
|
||||
name: 'square.and.arrow.up',
|
||||
name: 'plus',
|
||||
},
|
||||
android: 'ic_menu_share',
|
||||
web: 'share',
|
||||
android: '',
|
||||
web: 'plus',
|
||||
},
|
||||
})
|
||||
}
|
||||
items.push({
|
||||
testID: 'profileHeaderDropdownShareBtn',
|
||||
label: isWeb ? _(msg`Copy Link to Profile`) : _(msg`Share`),
|
||||
onPress: onPressShare,
|
||||
icon: {
|
||||
ios: {
|
||||
name: 'square.and.arrow.up',
|
||||
},
|
||||
android: 'ic_menu_share',
|
||||
web: 'share',
|
||||
},
|
||||
]
|
||||
})
|
||||
if (hasSession) {
|
||||
if (!profile.associated?.modservice) {
|
||||
items.push({label: 'separator'})
|
||||
items.push({
|
||||
testID: 'profileHeaderDropdownListAddRemoveBtn',
|
||||
label: _(msg`Add to Lists`),
|
||||
onPress: onPressAddRemoveLists,
|
||||
icon: {
|
||||
ios: {
|
||||
name: 'list.bullet',
|
||||
},
|
||||
android: 'ic_menu_add',
|
||||
web: 'list',
|
||||
items.push({label: 'separator'})
|
||||
items.push({
|
||||
testID: 'profileHeaderDropdownListAddRemoveBtn',
|
||||
label: _(msg`Add to Lists`),
|
||||
onPress: onPressAddRemoveLists,
|
||||
icon: {
|
||||
ios: {
|
||||
name: 'list.bullet',
|
||||
},
|
||||
})
|
||||
if (!isMe) {
|
||||
if (!profile.viewer?.blocking) {
|
||||
if (!profile.viewer?.mutedByList) {
|
||||
items.push({
|
||||
testID: 'profileHeaderDropdownMuteBtn',
|
||||
label: profile.viewer?.muted
|
||||
? _(msg`Unmute Account`)
|
||||
: _(msg`Mute Account`),
|
||||
onPress: profile.viewer?.muted
|
||||
? onPressUnmuteAccount
|
||||
: onPressMuteAccount,
|
||||
icon: {
|
||||
ios: {
|
||||
name: 'speaker.slash',
|
||||
},
|
||||
android: 'ic_lock_silent_mode',
|
||||
web: 'comment-slash',
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
if (!profile.viewer?.blockingByList) {
|
||||
android: 'ic_menu_add',
|
||||
web: 'list',
|
||||
},
|
||||
})
|
||||
if (!isMe) {
|
||||
if (!profile.viewer?.blocking) {
|
||||
if (!profile.viewer?.mutedByList) {
|
||||
items.push({
|
||||
testID: 'profileHeaderDropdownBlockBtn',
|
||||
label: profile.viewer?.blocking
|
||||
? _(msg`Unblock Account`)
|
||||
: _(msg`Block Account`),
|
||||
onPress: profile.viewer?.blocking
|
||||
? onPressUnblockAccount
|
||||
: onPressBlockAccount,
|
||||
testID: 'profileHeaderDropdownMuteBtn',
|
||||
label: profile.viewer?.muted
|
||||
? _(msg`Unmute Account`)
|
||||
: _(msg`Mute Account`),
|
||||
onPress: profile.viewer?.muted
|
||||
? onPressUnmuteAccount
|
||||
: onPressMuteAccount,
|
||||
icon: {
|
||||
ios: {
|
||||
name: 'person.fill.xmark',
|
||||
name: 'speaker.slash',
|
||||
},
|
||||
android: 'ic_menu_close_clear_cancel',
|
||||
web: 'user-slash',
|
||||
android: 'ic_lock_silent_mode',
|
||||
web: 'comment-slash',
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
if (!profile.viewer?.blockingByList) {
|
||||
items.push({
|
||||
testID: 'profileHeaderDropdownBlockBtn',
|
||||
label: profile.viewer?.blocking
|
||||
? _(msg`Unblock Account`)
|
||||
: _(msg`Block Account`),
|
||||
onPress: profile.viewer?.blocking
|
||||
? onPressUnblockAccount
|
||||
: onPressBlockAccount,
|
||||
icon: {
|
||||
ios: {
|
||||
name: 'person.fill.xmark',
|
||||
},
|
||||
android: 'ic_menu_close_clear_cancel',
|
||||
web: 'user-slash',
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
items.push({
|
||||
testID: 'profileHeaderDropdownReportBtn',
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import React from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {AppBskyActorDefs} from '@atproto/api'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
import {Shadow} from '#/state/cache/types'
|
||||
import {pluralize} from '#/lib/strings/helpers'
|
||||
import {makeProfileLink} from 'lib/routes/links'
|
||||
import {formatCount} from 'view/com/util/numeric/format'
|
||||
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {InlineLink} from '#/components/Link'
|
||||
|
||||
export function ProfileHeaderMetrics({
|
||||
profile,
|
||||
}: {
|
||||
profile: Shadow<AppBskyActorDefs.ProfileViewDetailed>
|
||||
}) {
|
||||
const t = useTheme()
|
||||
const {_} = useLingui()
|
||||
const following = formatCount(profile.followsCount || 0)
|
||||
const followers = formatCount(profile.followersCount || 0)
|
||||
const pluralizedFollowers = pluralize(profile.followersCount || 0, 'follower')
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[a.flex_row, a.gap_sm, a.align_center, a.pb_md]}
|
||||
pointerEvents="box-none">
|
||||
<InlineLink
|
||||
testID="profileHeaderFollowersButton"
|
||||
style={a.flex_row}
|
||||
to={makeProfileLink(profile, 'followers')}
|
||||
label={`${followers} ${pluralizedFollowers}`}>
|
||||
<Text style={[a.font_bold, t.atoms.text, a.text_md]}>{followers} </Text>
|
||||
<Text style={[t.atoms.text_contrast_medium, a.text_md]}>
|
||||
{pluralizedFollowers}
|
||||
</Text>
|
||||
</InlineLink>
|
||||
<InlineLink
|
||||
testID="profileHeaderFollowsButton"
|
||||
style={a.flex_row}
|
||||
to={makeProfileLink(profile, 'follows')}
|
||||
label={_(msg`${following} following`)}>
|
||||
<Trans>
|
||||
<Text style={[a.font_bold, t.atoms.text, a.text_md]}>
|
||||
{following}{' '}
|
||||
</Text>
|
||||
<Text style={[t.atoms.text_contrast_medium, a.text_md]}>
|
||||
following
|
||||
</Text>
|
||||
</Trans>
|
||||
</InlineLink>
|
||||
<Text style={[a.font_bold, t.atoms.text, a.text_md]}>
|
||||
{formatCount(profile.postsCount || 0)}{' '}
|
||||
<Text style={[t.atoms.text_contrast_medium, a.font_normal, a.text_md]}>
|
||||
{pluralize(profile.postsCount || 0, 'post')}
|
||||
</Text>
|
||||
</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import {ProfileHeaderShell} from './Shell'
|
||||
import {ProfileHeaderDropdownBtn} from './DropdownBtn'
|
||||
import {ProfileHeaderDisplayName} from './DisplayName'
|
||||
import {ProfileHeaderHandle} from './Handle'
|
||||
import {ProfileHeaderMetrics} from './Metrics'
|
||||
import {
|
||||
Heart2_Stroke2_Corner0_Rounded as Heart,
|
||||
Heart2_Filled_Stroke2_Corner0_Rounded as HeartFilled,
|
||||
@@ -170,7 +171,7 @@ let ProfileHeaderModerator = ({
|
||||
{isSubscribed ? (
|
||||
<Trans>Unsubscribe</Trans>
|
||||
) : (
|
||||
<Trans>Subscribe</Trans>
|
||||
<Trans>Subscribe to labeler</Trans>
|
||||
)}
|
||||
</ButtonText>
|
||||
</Button>
|
||||
@@ -184,7 +185,18 @@ let ProfileHeaderModerator = ({
|
||||
</View>
|
||||
{!isPlaceholderProfile && (
|
||||
<>
|
||||
<View style={[a.flex_row, a.gap_md, a.align_center, a.pb_md]}>
|
||||
<ProfileHeaderMetrics profile={profile} />
|
||||
{descriptionRT && !moderation.ui('profileView').blur ? (
|
||||
<View pointerEvents="auto">
|
||||
<RichText
|
||||
testID="profileHeaderDescription"
|
||||
style={t.atoms.text}
|
||||
numberOfLines={15}
|
||||
richText={descriptionRT}
|
||||
/>
|
||||
</View>
|
||||
) : undefined}
|
||||
<View style={[a.flex_row, a.gap_md, a.align_center, a.pt_md]}>
|
||||
<Button
|
||||
testID="toggleLikeBtn"
|
||||
size="small"
|
||||
@@ -212,16 +224,6 @@ let ProfileHeaderModerator = ({
|
||||
</InlineLink>
|
||||
)}
|
||||
</View>
|
||||
{descriptionRT && !moderation.ui('profileView').blur ? (
|
||||
<View pointerEvents="auto">
|
||||
<RichText
|
||||
testID="profileHeaderDescription"
|
||||
style={t.atoms.text}
|
||||
numberOfLines={15}
|
||||
richText={descriptionRT}
|
||||
/>
|
||||
</View>
|
||||
) : undefined}
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
|
||||
@@ -20,20 +20,16 @@ import {
|
||||
useProfileBlockMutationQueue,
|
||||
} from '#/state/queries/profile'
|
||||
import {logger} from '#/logger'
|
||||
import {pluralize} from '#/lib/strings/helpers'
|
||||
import {makeProfileLink} from 'lib/routes/links'
|
||||
import {formatCount} from 'view/com/util/numeric/format'
|
||||
import {sanitizeDisplayName} from 'lib/strings/display-names'
|
||||
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {InlineLink} from '#/components/Link'
|
||||
import {Button, ButtonText, ButtonIcon} from '#/components/Button'
|
||||
import * as Toast from '#/view/com/util/Toast'
|
||||
import {ProfileHeaderShell} from './Shell'
|
||||
import {ProfileHeaderDropdownBtn} from './DropdownBtn'
|
||||
import {ProfileHeaderDisplayName} from './DisplayName'
|
||||
import {ProfileHeaderHandle} from './Handle'
|
||||
import {ProfileHeaderMetrics} from './Metrics'
|
||||
import {ProfileHeaderSuggestedFollows} from '#/view/com/profile/ProfileHeaderSuggestedFollows'
|
||||
import {RichText} from 'view/com/util/text/RichText'
|
||||
import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check'
|
||||
@@ -146,9 +142,6 @@ let ProfileHeaderStandard = ({
|
||||
() => currentAccount?.did === profile.did,
|
||||
[currentAccount, profile],
|
||||
)
|
||||
const following = formatCount(profile.followsCount || 0)
|
||||
const followers = formatCount(profile.followersCount || 0)
|
||||
const pluralizedFollowers = pluralize(profile.followersCount || 0, 'follower')
|
||||
|
||||
return (
|
||||
<ProfileHeaderShell
|
||||
@@ -249,47 +242,7 @@ let ProfileHeaderStandard = ({
|
||||
</View>
|
||||
{!isPlaceholderProfile && (
|
||||
<>
|
||||
<View
|
||||
style={[a.flex_row, a.gap_sm, a.align_center, a.pb_md]}
|
||||
pointerEvents="box-none">
|
||||
<InlineLink
|
||||
testID="profileHeaderFollowersButton"
|
||||
style={a.flex_row}
|
||||
to={makeProfileLink(profile, 'followers')}
|
||||
label={`${followers} ${pluralizedFollowers}`}>
|
||||
<Text style={[a.font_bold, t.atoms.text, a.text_md]}>
|
||||
{followers}{' '}
|
||||
</Text>
|
||||
<Text style={[t.atoms.text_contrast_medium, a.text_md]}>
|
||||
{pluralizedFollowers}
|
||||
</Text>
|
||||
</InlineLink>
|
||||
<InlineLink
|
||||
testID="profileHeaderFollowsButton"
|
||||
style={a.flex_row}
|
||||
to={makeProfileLink(profile, 'follows')}
|
||||
label={_(msg`${following} following`)}>
|
||||
<Trans>
|
||||
<Text style={[a.font_bold, t.atoms.text, a.text_md]}>
|
||||
{following}{' '}
|
||||
</Text>
|
||||
<Text style={[t.atoms.text_contrast_medium, a.text_md]}>
|
||||
following
|
||||
</Text>
|
||||
</Trans>
|
||||
</InlineLink>
|
||||
<Text style={[a.font_bold, t.atoms.text, a.text_md]}>
|
||||
{formatCount(profile.postsCount || 0)}{' '}
|
||||
<Text
|
||||
style={[
|
||||
t.atoms.text_contrast_medium,
|
||||
a.font_normal,
|
||||
a.text_md,
|
||||
]}>
|
||||
{pluralize(profile.postsCount || 0, 'post')}
|
||||
</Text>
|
||||
</Text>
|
||||
</View>
|
||||
<ProfileHeaderMetrics profile={profile} />
|
||||
{descriptionRT && !moderation.ui('profileView').blur ? (
|
||||
<View pointerEvents="auto">
|
||||
<RichText
|
||||
|
||||
@@ -113,8 +113,10 @@ let ProfileHeaderShell = ({
|
||||
t.atoms.bg,
|
||||
{borderColor: t.atoms.bg.backgroundColor},
|
||||
styles.avi,
|
||||
profile.associated?.modservice && styles.aviLabeler,
|
||||
]}>
|
||||
<UserAvatar
|
||||
type={profile.associated?.modservice ? 'labeler' : 'user'}
|
||||
size={90}
|
||||
avatar={profile.avatar}
|
||||
moderation={moderation.ui('avatar')}
|
||||
@@ -155,4 +157,7 @@ const styles = StyleSheet.create({
|
||||
borderRadius: 47,
|
||||
borderWidth: 2,
|
||||
},
|
||||
aviLabeler: {
|
||||
borderRadius: 12,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -42,12 +42,12 @@ interface Props {
|
||||
}
|
||||
|
||||
let ProfileHeader = (props: Props): React.ReactNode => {
|
||||
// if (props.profile.associated?.modservice) {
|
||||
// if (!props.modservice) {
|
||||
// return <ProfileHeaderLoading />
|
||||
// }
|
||||
// return <ProfileHeaderModerator {...props} modservice={props.modservice} />
|
||||
// }
|
||||
if (props.profile.associated?.modservice) {
|
||||
if (!props.modservice) {
|
||||
return <ProfileHeaderLoading />
|
||||
}
|
||||
return <ProfileHeaderModerator {...props} modservice={props.modservice} />
|
||||
}
|
||||
return <ProfileHeaderStandard {...props} />
|
||||
}
|
||||
ProfileHeader = memo(ProfileHeader)
|
||||
|
||||
@@ -113,51 +113,26 @@ export function ProfileContentFiltersSectionInner({
|
||||
borderWidth: 0,
|
||||
paddingHorizontal: a.px_xl.paddingLeft,
|
||||
}}>
|
||||
<View style={[a.flex_row, a.gap_md, a.pt_xl]}>
|
||||
<View style={[a.flex_1]}>
|
||||
<Text style={[t.atoms.text_contrast_high, a.leading_snug, a.text_md]}>
|
||||
<View style={[a.pt_xl]}>
|
||||
<Text style={[t.atoms.text_contrast_high, a.leading_snug, a.text_sm]}>
|
||||
<Trans>
|
||||
Labels are annotations on users and content. They can be used to
|
||||
hide, warn, and categorize the network.
|
||||
</Trans>
|
||||
</Text>
|
||||
{!isSubscribed && (
|
||||
<Text
|
||||
style={[
|
||||
a.pt_xl,
|
||||
t.atoms.text_contrast_high,
|
||||
a.leading_snug,
|
||||
a.text_sm,
|
||||
]}>
|
||||
<Trans>
|
||||
Labels are annotations on users and content. They can be used to
|
||||
hide, warn, and categorize the network.
|
||||
Subscribe to @{modservice.creator.handle} to use these labels:
|
||||
</Trans>
|
||||
</Text>
|
||||
{!isSubscribed && (
|
||||
<Text
|
||||
style={[
|
||||
a.pt_xl,
|
||||
t.atoms.text_contrast_high,
|
||||
a.leading_snug,
|
||||
a.text_md,
|
||||
]}>
|
||||
<Trans>
|
||||
Subscribe to use these labels from @{modservice.creator.handle}:
|
||||
</Trans>
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
<View>
|
||||
<Button
|
||||
testID="toggleSubscribeBtn"
|
||||
size="small"
|
||||
color={isSubscribed ? 'secondary' : 'primary'}
|
||||
variant="solid"
|
||||
label={
|
||||
isSubscribed
|
||||
? _(msg`Unsubscribe from this labeler`)
|
||||
: _(msg`Subscribe to this labeler`)
|
||||
}
|
||||
disabled={!hasSession}
|
||||
onPress={onPressSubscribe}
|
||||
style={a.rounded_full}>
|
||||
<ButtonText>
|
||||
{isSubscribed ? (
|
||||
<Trans>Unsubscribe</Trans>
|
||||
) : (
|
||||
<Trans>Subscribe</Trans>
|
||||
)}
|
||||
</ButtonText>
|
||||
</Button>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
<View
|
||||
style={[
|
||||
|
||||
Reference in New Issue
Block a user