Add user/profile labeling
This commit is contained in:
@@ -104,7 +104,7 @@ export const LABEL_VAL_GROUPS: Record<string, LabelValGroup> = {
|
|||||||
},
|
},
|
||||||
impersonation: {
|
impersonation: {
|
||||||
id: 'impersonation',
|
id: 'impersonation',
|
||||||
title: 'Impersonated Content',
|
title: 'Impersonation',
|
||||||
values: ['impersonation'],
|
values: ['impersonation'],
|
||||||
},
|
},
|
||||||
unknown: {
|
unknown: {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import {ComAtprotoLabelDefs} from '@atproto/api'
|
|
||||||
import {LabelValGroup, LABEL_VAL_GROUPS} from './const'
|
import {LabelValGroup, LABEL_VAL_GROUPS} from './const'
|
||||||
|
|
||||||
export function getLabelValueGroup(labelVal: string): LabelValGroup {
|
export function getLabelValueGroup(labelVal: string): LabelValGroup {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import {makeAutoObservable, runInAction} from 'mobx'
|
import {makeAutoObservable, runInAction} from 'mobx'
|
||||||
import {PickedMedia} from 'lib/media/picker'
|
import {PickedMedia} from 'lib/media/picker'
|
||||||
import {
|
import {
|
||||||
|
ComAtprotoLabelDefs,
|
||||||
AppBskyActorGetProfile as GetProfile,
|
AppBskyActorGetProfile as GetProfile,
|
||||||
AppBskyActorProfile,
|
AppBskyActorProfile,
|
||||||
RichText,
|
RichText,
|
||||||
@@ -41,6 +42,7 @@ export class ProfileModel {
|
|||||||
followersCount: number = 0
|
followersCount: number = 0
|
||||||
followsCount: number = 0
|
followsCount: number = 0
|
||||||
postsCount: number = 0
|
postsCount: number = 0
|
||||||
|
labels?: ComAtprotoLabelDefs.Label[] = undefined
|
||||||
viewer = new ProfileViewerModel()
|
viewer = new ProfileViewerModel()
|
||||||
|
|
||||||
// added data
|
// added data
|
||||||
@@ -210,6 +212,7 @@ export class ProfileModel {
|
|||||||
this.followersCount = res.data.followersCount || 0
|
this.followersCount = res.data.followersCount || 0
|
||||||
this.followsCount = res.data.followsCount || 0
|
this.followsCount = res.data.followsCount || 0
|
||||||
this.postsCount = res.data.postsCount || 0
|
this.postsCount = res.data.postsCount || 0
|
||||||
|
this.labels = res.data.labels
|
||||||
if (res.data.viewer) {
|
if (res.data.viewer) {
|
||||||
Object.assign(this.viewer, res.data.viewer)
|
Object.assign(this.viewer, res.data.viewer)
|
||||||
this.rootStore.me.follows.hydrate(this.did, res.data.viewer.following)
|
this.rootStore.me.follows.hydrate(this.did, res.data.viewer.following)
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ export const SuggestedFollows = ({
|
|||||||
handle={item.handle}
|
handle={item.handle}
|
||||||
displayName={item.displayName}
|
displayName={item.displayName}
|
||||||
avatar={item.avatar}
|
avatar={item.avatar}
|
||||||
|
labels={item.labels}
|
||||||
noBg
|
noBg
|
||||||
noBorder
|
noBorder
|
||||||
description={
|
description={
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
View,
|
View,
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import {AppBskyEmbedImages} from '@atproto/api'
|
import {AppBskyEmbedImages} from '@atproto/api'
|
||||||
import {AtUri} from '@atproto/api'
|
import {AtUri, ComAtprotoLabelDefs} from '@atproto/api'
|
||||||
import {
|
import {
|
||||||
FontAwesomeIcon,
|
FontAwesomeIcon,
|
||||||
FontAwesomeIconStyle,
|
FontAwesomeIconStyle,
|
||||||
@@ -38,6 +38,7 @@ interface Author {
|
|||||||
handle: string
|
handle: string
|
||||||
displayName?: string
|
displayName?: string
|
||||||
avatar?: string
|
avatar?: string
|
||||||
|
labels?: ComAtprotoLabelDefs.Label[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FeedItem = observer(function FeedItem({
|
export const FeedItem = observer(function FeedItem({
|
||||||
@@ -129,6 +130,7 @@ export const FeedItem = observer(function FeedItem({
|
|||||||
handle: item.author.handle,
|
handle: item.author.handle,
|
||||||
displayName: item.author.displayName,
|
displayName: item.author.displayName,
|
||||||
avatar: item.author.avatar,
|
avatar: item.author.avatar,
|
||||||
|
labels: item.author.labels,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
if (item.additional?.length) {
|
if (item.additional?.length) {
|
||||||
@@ -138,6 +140,7 @@ export const FeedItem = observer(function FeedItem({
|
|||||||
handle: item2.author.handle,
|
handle: item2.author.handle,
|
||||||
displayName: item2.author.displayName,
|
displayName: item2.author.displayName,
|
||||||
avatar: item2.author.avatar,
|
avatar: item2.author.avatar,
|
||||||
|
labels: item.author.labels,
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -255,7 +258,11 @@ function CondensedAuthorsList({
|
|||||||
href={authors[0].href}
|
href={authors[0].href}
|
||||||
title={`@${authors[0].handle}`}
|
title={`@${authors[0].handle}`}
|
||||||
asAnchor>
|
asAnchor>
|
||||||
<UserAvatar size={35} avatar={authors[0].avatar} />
|
<UserAvatar
|
||||||
|
size={35}
|
||||||
|
avatar={authors[0].avatar}
|
||||||
|
hasWarning={!!authors[0].labels?.length}
|
||||||
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
@@ -264,7 +271,11 @@ function CondensedAuthorsList({
|
|||||||
<View style={styles.avis}>
|
<View style={styles.avis}>
|
||||||
{authors.slice(0, MAX_AUTHORS).map(author => (
|
{authors.slice(0, MAX_AUTHORS).map(author => (
|
||||||
<View key={author.href} style={s.mr5}>
|
<View key={author.href} style={s.mr5}>
|
||||||
<UserAvatar size={35} avatar={author.avatar} />
|
<UserAvatar
|
||||||
|
size={35}
|
||||||
|
avatar={author.avatar}
|
||||||
|
hasWarning={!!author.labels?.length}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
))}
|
))}
|
||||||
{authors.length > MAX_AUTHORS ? (
|
{authors.length > MAX_AUTHORS ? (
|
||||||
@@ -317,7 +328,11 @@ function ExpandedAuthorsList({
|
|||||||
style={styles.expandedAuthor}
|
style={styles.expandedAuthor}
|
||||||
asAnchor>
|
asAnchor>
|
||||||
<View style={styles.expandedAuthorAvi}>
|
<View style={styles.expandedAuthorAvi}>
|
||||||
<UserAvatar size={35} avatar={author.avatar} />
|
<UserAvatar
|
||||||
|
size={35}
|
||||||
|
avatar={author.avatar}
|
||||||
|
hasWarning={!!author.labels?.length}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={s.flex1}>
|
<View style={s.flex1}>
|
||||||
<Text
|
<Text
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ export const PostLikedBy = observer(function ({uri}: {uri: string}) {
|
|||||||
handle={item.actor.handle}
|
handle={item.actor.handle}
|
||||||
displayName={item.actor.displayName}
|
displayName={item.actor.displayName}
|
||||||
avatar={item.actor.avatar}
|
avatar={item.actor.avatar}
|
||||||
|
labels={item.actor.labels}
|
||||||
isFollowedBy={!!item.actor.viewer?.followedBy}
|
isFollowedBy={!!item.actor.viewer?.followedBy}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ export const PostRepostedBy = observer(function PostRepostedBy({
|
|||||||
handle={item.handle}
|
handle={item.handle}
|
||||||
displayName={item.displayName}
|
displayName={item.displayName}
|
||||||
avatar={item.avatar}
|
avatar={item.avatar}
|
||||||
|
labels={item.labels}
|
||||||
isFollowedBy={!!item.viewer?.followedBy}
|
isFollowedBy={!!item.viewer?.followedBy}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -138,7 +138,11 @@ export const PostThreadItem = observer(function PostThreadItem({
|
|||||||
<View style={styles.layout}>
|
<View style={styles.layout}>
|
||||||
<View style={styles.layoutAvi}>
|
<View style={styles.layoutAvi}>
|
||||||
<Link href={authorHref} title={authorTitle} asAnchor>
|
<Link href={authorHref} title={authorTitle} asAnchor>
|
||||||
<UserAvatar size={52} avatar={item.post.author.avatar} />
|
<UserAvatar
|
||||||
|
size={52}
|
||||||
|
avatar={item.post.author.avatar}
|
||||||
|
hasWarning={!!item.post.author.labels?.length}
|
||||||
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.layoutContent}>
|
<View style={styles.layoutContent}>
|
||||||
@@ -304,13 +308,18 @@ export const PostThreadItem = observer(function PostThreadItem({
|
|||||||
<View style={styles.layout}>
|
<View style={styles.layout}>
|
||||||
<View style={styles.layoutAvi}>
|
<View style={styles.layoutAvi}>
|
||||||
<Link href={authorHref} title={authorTitle} asAnchor>
|
<Link href={authorHref} title={authorTitle} asAnchor>
|
||||||
<UserAvatar size={52} avatar={item.post.author.avatar} />
|
<UserAvatar
|
||||||
|
size={52}
|
||||||
|
avatar={item.post.author.avatar}
|
||||||
|
hasWarning={!!item.post.author.labels?.length}
|
||||||
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.layoutContent}>
|
<View style={styles.layoutContent}>
|
||||||
<PostMeta
|
<PostMeta
|
||||||
authorHandle={item.post.author.handle}
|
authorHandle={item.post.author.handle}
|
||||||
authorDisplayName={item.post.author.displayName}
|
authorDisplayName={item.post.author.displayName}
|
||||||
|
authorHasWarning={!!item.post.author.labels?.length}
|
||||||
timestamp={item.post.indexedAt}
|
timestamp={item.post.indexedAt}
|
||||||
postHref={itemHref}
|
postHref={itemHref}
|
||||||
did={item.post.author.did}
|
did={item.post.author.did}
|
||||||
|
|||||||
@@ -96,7 +96,6 @@ export const Post = observer(function Post({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PostLoaded
|
<PostLoaded
|
||||||
view={view}
|
|
||||||
item={view.thread}
|
item={view.thread}
|
||||||
record={view.thread.postRecord}
|
record={view.thread.postRecord}
|
||||||
setDeleted={setDeleted}
|
setDeleted={setDeleted}
|
||||||
@@ -198,13 +197,18 @@ const PostLoaded = observer(
|
|||||||
<View style={styles.layout}>
|
<View style={styles.layout}>
|
||||||
<View style={styles.layoutAvi}>
|
<View style={styles.layoutAvi}>
|
||||||
<Link href={authorHref} title={authorTitle} asAnchor>
|
<Link href={authorHref} title={authorTitle} asAnchor>
|
||||||
<UserAvatar size={52} avatar={item.post.author.avatar} />
|
<UserAvatar
|
||||||
|
size={52}
|
||||||
|
avatar={item.post.author.avatar}
|
||||||
|
hasWarning={!!item.post.author.labels?.length}
|
||||||
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.layoutContent}>
|
<View style={styles.layoutContent}>
|
||||||
<PostMeta
|
<PostMeta
|
||||||
authorHandle={item.post.author.handle}
|
authorHandle={item.post.author.handle}
|
||||||
authorDisplayName={item.post.author.displayName}
|
authorDisplayName={item.post.author.displayName}
|
||||||
|
authorHasWarning={!!item.post.author.labels?.length}
|
||||||
timestamp={item.post.indexedAt}
|
timestamp={item.post.indexedAt}
|
||||||
postHref={itemHref}
|
postHref={itemHref}
|
||||||
did={item.post.author.did}
|
did={item.post.author.did}
|
||||||
|
|||||||
@@ -189,13 +189,18 @@ export const FeedItem = observer(function ({
|
|||||||
<View style={styles.layout}>
|
<View style={styles.layout}>
|
||||||
<View style={styles.layoutAvi}>
|
<View style={styles.layoutAvi}>
|
||||||
<Link href={authorHref} title={item.post.author.handle} asAnchor>
|
<Link href={authorHref} title={item.post.author.handle} asAnchor>
|
||||||
<UserAvatar size={52} avatar={item.post.author.avatar} />
|
<UserAvatar
|
||||||
|
size={52}
|
||||||
|
avatar={item.post.author.avatar}
|
||||||
|
hasWarning={!!item.post.author.labels?.length}
|
||||||
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.layoutContent}>
|
<View style={styles.layoutContent}>
|
||||||
<PostMeta
|
<PostMeta
|
||||||
authorHandle={item.post.author.handle}
|
authorHandle={item.post.author.handle}
|
||||||
authorDisplayName={item.post.author.displayName}
|
authorDisplayName={item.post.author.displayName}
|
||||||
|
authorHasWarning={!!item.post.author.labels?.length}
|
||||||
timestamp={item.post.indexedAt}
|
timestamp={item.post.indexedAt}
|
||||||
postHref={itemHref}
|
postHref={itemHref}
|
||||||
did={item.post.author.did}
|
did={item.post.author.did}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {StyleSheet, View} from 'react-native'
|
import {StyleSheet, View} from 'react-native'
|
||||||
import {observer} from 'mobx-react-lite'
|
import {observer} from 'mobx-react-lite'
|
||||||
import {AppBskyActorDefs} from '@atproto/api'
|
import {AppBskyActorDefs, ComAtprotoLabelDefs} from '@atproto/api'
|
||||||
import {Link} from '../util/Link'
|
import {Link} from '../util/Link'
|
||||||
import {Text} from '../util/text/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {UserAvatar} from '../util/UserAvatar'
|
import {UserAvatar} from '../util/UserAvatar'
|
||||||
@@ -17,6 +17,7 @@ export function ProfileCard({
|
|||||||
displayName,
|
displayName,
|
||||||
avatar,
|
avatar,
|
||||||
description,
|
description,
|
||||||
|
labels,
|
||||||
isFollowedBy,
|
isFollowedBy,
|
||||||
noBg,
|
noBg,
|
||||||
noBorder,
|
noBorder,
|
||||||
@@ -28,6 +29,7 @@ export function ProfileCard({
|
|||||||
displayName?: string
|
displayName?: string
|
||||||
avatar?: string
|
avatar?: string
|
||||||
description?: string
|
description?: string
|
||||||
|
labels: ComAtprotoLabelDefs.Label[] | undefined
|
||||||
isFollowedBy?: boolean
|
isFollowedBy?: boolean
|
||||||
noBg?: boolean
|
noBg?: boolean
|
||||||
noBorder?: boolean
|
noBorder?: boolean
|
||||||
@@ -50,7 +52,7 @@ export function ProfileCard({
|
|||||||
asAnchor>
|
asAnchor>
|
||||||
<View style={styles.layout}>
|
<View style={styles.layout}>
|
||||||
<View style={styles.layoutAvi}>
|
<View style={styles.layoutAvi}>
|
||||||
<UserAvatar size={40} avatar={avatar} />
|
<UserAvatar size={40} avatar={avatar} hasWarning={!!labels?.length} />
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.layoutContent}>
|
<View style={styles.layoutContent}>
|
||||||
<Text
|
<Text
|
||||||
@@ -114,6 +116,7 @@ export const ProfileCardWithFollowBtn = observer(
|
|||||||
displayName,
|
displayName,
|
||||||
avatar,
|
avatar,
|
||||||
description,
|
description,
|
||||||
|
labels,
|
||||||
isFollowedBy,
|
isFollowedBy,
|
||||||
noBg,
|
noBg,
|
||||||
noBorder,
|
noBorder,
|
||||||
@@ -124,6 +127,7 @@ export const ProfileCardWithFollowBtn = observer(
|
|||||||
displayName?: string
|
displayName?: string
|
||||||
avatar?: string
|
avatar?: string
|
||||||
description?: string
|
description?: string
|
||||||
|
labels: ComAtprotoLabelDefs.Label[] | undefined
|
||||||
isFollowedBy?: boolean
|
isFollowedBy?: boolean
|
||||||
noBg?: boolean
|
noBg?: boolean
|
||||||
noBorder?: boolean
|
noBorder?: boolean
|
||||||
@@ -138,6 +142,7 @@ export const ProfileCardWithFollowBtn = observer(
|
|||||||
displayName={displayName}
|
displayName={displayName}
|
||||||
avatar={avatar}
|
avatar={avatar}
|
||||||
description={description}
|
description={description}
|
||||||
|
labels={labels}
|
||||||
isFollowedBy={isFollowedBy}
|
isFollowedBy={isFollowedBy}
|
||||||
noBg={noBg}
|
noBg={noBg}
|
||||||
noBorder={noBorder}
|
noBorder={noBorder}
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ export const ProfileFollowers = observer(function ProfileFollowers({
|
|||||||
handle={item.handle}
|
handle={item.handle}
|
||||||
displayName={item.displayName}
|
displayName={item.displayName}
|
||||||
avatar={item.avatar}
|
avatar={item.avatar}
|
||||||
|
labels={item.labels}
|
||||||
isFollowedBy={!!item.viewer?.followedBy}
|
isFollowedBy={!!item.viewer?.followedBy}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ export const ProfileFollows = observer(function ProfileFollows({
|
|||||||
handle={item.handle}
|
handle={item.handle}
|
||||||
displayName={item.displayName}
|
displayName={item.displayName}
|
||||||
avatar={item.avatar}
|
avatar={item.avatar}
|
||||||
|
labels={item.labels}
|
||||||
isFollowedBy={!!item.viewer?.followedBy}
|
isFollowedBy={!!item.viewer?.followedBy}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import {Text} from '../util/text/Text'
|
|||||||
import {RichText} from '../util/text/RichText'
|
import {RichText} from '../util/text/RichText'
|
||||||
import {UserAvatar} from '../util/UserAvatar'
|
import {UserAvatar} from '../util/UserAvatar'
|
||||||
import {UserBanner} from '../util/UserBanner'
|
import {UserBanner} from '../util/UserBanner'
|
||||||
|
import {ProfileHeaderLabels} from '../util/moderation/ProfileHeaderLabels'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {useAnalytics} from 'lib/analytics'
|
import {useAnalytics} from 'lib/analytics'
|
||||||
import {NavigationProp} from 'lib/routes/types'
|
import {NavigationProp} from 'lib/routes/types'
|
||||||
@@ -320,6 +321,7 @@ const ProfileHeaderLoaded = observer(function ProfileHeaderLoaded({
|
|||||||
richText={view.descriptionRichText}
|
richText={view.descriptionRichText}
|
||||||
/>
|
/>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
|
<ProfileHeaderLabels labels={view.labels} />
|
||||||
{view.viewer.muted ? (
|
{view.viewer.muted ? (
|
||||||
<View
|
<View
|
||||||
testID="profileHeaderMutedNotice"
|
testID="profileHeaderMutedNotice"
|
||||||
@@ -348,7 +350,11 @@ const ProfileHeaderLoaded = observer(function ProfileHeaderLoaded({
|
|||||||
onPress={onPressAvi}>
|
onPress={onPressAvi}>
|
||||||
<View
|
<View
|
||||||
style={[pal.view, {borderColor: pal.colors.background}, styles.avi]}>
|
style={[pal.view, {borderColor: pal.colors.background}, styles.avi]}>
|
||||||
<UserAvatar size={80} avatar={view.avatar} />
|
<UserAvatar
|
||||||
|
size={80}
|
||||||
|
avatar={view.avatar}
|
||||||
|
hasWarning={!!view.labels?.length}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
</TouchableWithoutFeedback>
|
</TouchableWithoutFeedback>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ const Profiles = observer(({model}: {model: SearchUIModel}) => {
|
|||||||
displayName={item.displayName}
|
displayName={item.displayName}
|
||||||
avatar={item.avatar}
|
avatar={item.avatar}
|
||||||
description={item.description}
|
description={item.description}
|
||||||
|
labels={item.labels}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<View style={s.footerSpacer} />
|
<View style={s.footerSpacer} />
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ interface PostMetaOpts {
|
|||||||
authorAvatar?: string
|
authorAvatar?: string
|
||||||
authorHandle: string
|
authorHandle: string
|
||||||
authorDisplayName: string | undefined
|
authorDisplayName: string | undefined
|
||||||
|
authorHasWarning: boolean
|
||||||
postHref: string
|
postHref: string
|
||||||
timestamp: string
|
timestamp: string
|
||||||
did?: string
|
did?: string
|
||||||
@@ -93,7 +94,11 @@ export const PostMeta = observer(function (opts: PostMetaOpts) {
|
|||||||
<View style={styles.meta}>
|
<View style={styles.meta}>
|
||||||
{typeof opts.authorAvatar !== 'undefined' && (
|
{typeof opts.authorAvatar !== 'undefined' && (
|
||||||
<View style={[styles.metaItem, styles.avatar]}>
|
<View style={[styles.metaItem, styles.avatar]}>
|
||||||
<UserAvatar avatar={opts.authorAvatar} size={16} />
|
<UserAvatar
|
||||||
|
avatar={opts.authorAvatar}
|
||||||
|
size={16}
|
||||||
|
hasWarning={opts.authorHasWarning}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
<View style={[styles.metaItem, styles.maxWidth]}>
|
<View style={[styles.metaItem, styles.maxWidth]}>
|
||||||
|
|||||||
@@ -44,10 +44,12 @@ function DefaultAvatar({size}: {size: number}) {
|
|||||||
export function UserAvatar({
|
export function UserAvatar({
|
||||||
size,
|
size,
|
||||||
avatar,
|
avatar,
|
||||||
|
hasWarning,
|
||||||
onSelectNewAvatar,
|
onSelectNewAvatar,
|
||||||
}: {
|
}: {
|
||||||
size: number
|
size: number
|
||||||
avatar?: string | null
|
avatar?: string | null
|
||||||
|
hasWarning?: boolean
|
||||||
onSelectNewAvatar?: (img: PickedMedia | null) => void
|
onSelectNewAvatar?: (img: PickedMedia | null) => void
|
||||||
}) {
|
}) {
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
@@ -105,6 +107,22 @@ export function UserAvatar({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const warning = React.useMemo(() => {
|
||||||
|
if (!hasWarning) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<View style={[styles.warningIconContainer, pal.view]}>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon="exclamation-circle"
|
||||||
|
style={styles.warningIcon}
|
||||||
|
size={Math.floor(size / 3)}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}, [hasWarning, size, pal])
|
||||||
|
|
||||||
// onSelectNewAvatar is only passed as prop on the EditProfile component
|
// onSelectNewAvatar is only passed as prop on the EditProfile component
|
||||||
return onSelectNewAvatar ? (
|
return onSelectNewAvatar ? (
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
@@ -137,14 +155,20 @@ export function UserAvatar({
|
|||||||
</View>
|
</View>
|
||||||
</DropdownButton>
|
</DropdownButton>
|
||||||
) : avatar ? (
|
) : avatar ? (
|
||||||
<HighPriorityImage
|
<View style={{width: size, height: size}}>
|
||||||
testID="userAvatarImage"
|
<HighPriorityImage
|
||||||
style={{width: size, height: size, borderRadius: Math.floor(size / 2)}}
|
testID="userAvatarImage"
|
||||||
resizeMode="stretch"
|
style={{width: size, height: size, borderRadius: Math.floor(size / 2)}}
|
||||||
source={{uri: avatar}}
|
resizeMode="stretch"
|
||||||
/>
|
source={{uri: avatar}}
|
||||||
|
/>
|
||||||
|
{warning}
|
||||||
|
</View>
|
||||||
) : (
|
) : (
|
||||||
<DefaultAvatar size={size} />
|
<View style={{width: size, height: size}}>
|
||||||
|
<DefaultAvatar size={size} />
|
||||||
|
{warning}
|
||||||
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,4 +189,13 @@ const styles = StyleSheet.create({
|
|||||||
height: 80,
|
height: 80,
|
||||||
borderRadius: 40,
|
borderRadius: 40,
|
||||||
},
|
},
|
||||||
|
warningIconContainer: {
|
||||||
|
position: 'absolute',
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
borderRadius: 100,
|
||||||
|
},
|
||||||
|
warningIcon: {
|
||||||
|
color: colors.red3,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import {StyleSheet, View} from 'react-native'
|
||||||
|
import {ComAtprotoLabelDefs} from '@atproto/api'
|
||||||
|
import {
|
||||||
|
FontAwesomeIcon,
|
||||||
|
FontAwesomeIconStyle,
|
||||||
|
} from '@fortawesome/react-native-fontawesome'
|
||||||
|
import {Text} from '../text/Text'
|
||||||
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
|
import {getLabelValueGroup} from 'lib/labeling/helpers'
|
||||||
|
|
||||||
|
export function ProfileHeaderLabels({
|
||||||
|
labels,
|
||||||
|
}: {
|
||||||
|
labels: ComAtprotoLabelDefs.Label[] | undefined
|
||||||
|
}) {
|
||||||
|
const palErr = usePalette('error')
|
||||||
|
if (!labels?.length) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{labels.map((label, i) => {
|
||||||
|
const labelGroup = getLabelValueGroup(label?.val || '')
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
key={`${label.val}-${i}`}
|
||||||
|
style={[styles.container, palErr.border, palErr.view]}>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon="circle-exclamation"
|
||||||
|
style={palErr.text as FontAwesomeIconStyle}
|
||||||
|
size={20}
|
||||||
|
/>
|
||||||
|
<Text style={palErr.text}>
|
||||||
|
This account has been flagged for{' '}
|
||||||
|
{labelGroup.title.toLocaleLowerCase()}.
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 10,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderRadius: 6,
|
||||||
|
paddingHorizontal: 10,
|
||||||
|
paddingVertical: 8,
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -42,6 +42,7 @@ export function QuoteEmbed({
|
|||||||
authorAvatar={quote.author.avatar}
|
authorAvatar={quote.author.avatar}
|
||||||
authorHandle={quote.author.handle}
|
authorHandle={quote.author.handle}
|
||||||
authorDisplayName={quote.author.displayName}
|
authorDisplayName={quote.author.displayName}
|
||||||
|
authorHasWarning={false}
|
||||||
postHref={itemHref}
|
postHref={itemHref}
|
||||||
timestamp={quote.indexedAt}
|
timestamp={quote.indexedAt}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -155,6 +155,7 @@ export const SearchScreen = withAuthRequired(
|
|||||||
testID={`searchAutoCompleteResult-${item.handle}`}
|
testID={`searchAutoCompleteResult-${item.handle}`}
|
||||||
handle={item.handle}
|
handle={item.handle}
|
||||||
displayName={item.displayName}
|
displayName={item.displayName}
|
||||||
|
labels={item.labels}
|
||||||
avatar={item.avatar}
|
avatar={item.avatar}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ export const DesktopSearch = observer(function DesktopSearch() {
|
|||||||
handle={item.handle}
|
handle={item.handle}
|
||||||
displayName={item.displayName}
|
displayName={item.displayName}
|
||||||
avatar={item.avatar}
|
avatar={item.avatar}
|
||||||
|
labels={item.labels}
|
||||||
noBorder={i === 0}
|
noBorder={i === 0}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|||||||
Reference in New Issue
Block a user