remove precacheThreadPostProfiles (#3729)
* remove `precacheThreadPostProfiles` * add `displayName` to `PreviewableUserAvatar` * memo * use `precacheProfile` * pass `profile` directly to `PreviewableUserAvatar` * update the `UserAvatar`'s props * remove feed cache * one more spot * rm unused queryClient * Don't call fn unnecessarily * Preload for display name too * try notification item * add to feeditem * and finally, precache for post threads * timestamp * Fix * onBeforePress --------- Co-authored-by: Dan Abramov <dan.abramov@gmail.com>zio/stable
parent
ce85375c85
commit
7eb1444f2c
|
@ -1,25 +1,27 @@
|
||||||
import React, {ComponentProps} from 'react'
|
import React, {ComponentProps} from 'react'
|
||||||
import {StyleSheet, Pressable, View, ViewStyle, StyleProp} from 'react-native'
|
import {Pressable, StyleProp, StyleSheet, View, ViewStyle} from 'react-native'
|
||||||
import {ModerationUI} from '@atproto/api'
|
import {AppBskyActorDefs, ModerationUI} from '@atproto/api'
|
||||||
|
import {msg, Trans} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
import {Trans, msg} from '@lingui/macro'
|
import {useQueryClient} from '@tanstack/react-query'
|
||||||
|
|
||||||
import {useModerationCauseDescription} from '#/lib/moderation/useModerationCauseDescription'
|
import {useModerationCauseDescription} from '#/lib/moderation/useModerationCauseDescription'
|
||||||
import {addStyle} from 'lib/styles'
|
import {addStyle} from 'lib/styles'
|
||||||
|
import {precacheProfile} from 'state/queries/profile'
|
||||||
import {useTheme, atoms as a} from '#/alf'
|
// import {Link} from '#/components/Link' TODO this imposes some styles that screw things up
|
||||||
|
import {Link} from '#/view/com/util/Link'
|
||||||
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
import {
|
import {
|
||||||
ModerationDetailsDialog,
|
ModerationDetailsDialog,
|
||||||
useModerationDetailsDialogControl,
|
useModerationDetailsDialogControl,
|
||||||
} from '#/components/moderation/ModerationDetailsDialog'
|
} from '#/components/moderation/ModerationDetailsDialog'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
// import {Link} from '#/components/Link' TODO this imposes some styles that screw things up
|
|
||||||
import {Link} from '#/view/com/util/Link'
|
|
||||||
|
|
||||||
interface Props extends ComponentProps<typeof Link> {
|
interface Props extends ComponentProps<typeof Link> {
|
||||||
iconSize: number
|
iconSize: number
|
||||||
iconStyles: StyleProp<ViewStyle>
|
iconStyles: StyleProp<ViewStyle>
|
||||||
modui: ModerationUI
|
modui: ModerationUI
|
||||||
|
profile: AppBskyActorDefs.ProfileViewBasic
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PostHider({
|
export function PostHider({
|
||||||
|
@ -30,8 +32,10 @@ export function PostHider({
|
||||||
children,
|
children,
|
||||||
iconSize,
|
iconSize,
|
||||||
iconStyles,
|
iconStyles,
|
||||||
|
profile,
|
||||||
...props
|
...props
|
||||||
}: Props) {
|
}: Props) {
|
||||||
|
const queryClient = useQueryClient()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const [override, setOverride] = React.useState(false)
|
const [override, setOverride] = React.useState(false)
|
||||||
|
@ -39,6 +43,10 @@ export function PostHider({
|
||||||
const blur = modui.blurs[0]
|
const blur = modui.blurs[0]
|
||||||
const desc = useModerationCauseDescription(blur)
|
const desc = useModerationCauseDescription(blur)
|
||||||
|
|
||||||
|
const onBeforePress = React.useCallback(() => {
|
||||||
|
precacheProfile(queryClient, profile)
|
||||||
|
}, [queryClient, profile])
|
||||||
|
|
||||||
if (!blur) {
|
if (!blur) {
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
|
@ -46,6 +54,7 @@ export function PostHider({
|
||||||
style={style}
|
style={style}
|
||||||
href={href}
|
href={href}
|
||||||
accessible={false}
|
accessible={false}
|
||||||
|
onBeforePress={onBeforePress}
|
||||||
{...props}>
|
{...props}>
|
||||||
{children}
|
{children}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
@ -113,12 +113,7 @@ export function MessagesListScreen({}: Props) {
|
||||||
<Link
|
<Link
|
||||||
to={`/messages/${item.profile.handle}`}
|
to={`/messages/${item.profile.handle}`}
|
||||||
style={[a.flex_1, a.pl_md, a.py_sm, a.gap_md, a.pr_2xl]}>
|
style={[a.flex_1, a.pl_md, a.py_sm, a.gap_md, a.pr_2xl]}>
|
||||||
<PreviewableUserAvatar
|
<PreviewableUserAvatar profile={item.profile} size={44} />
|
||||||
did={item.profile.did}
|
|
||||||
handle={item.profile.handle}
|
|
||||||
size={44}
|
|
||||||
avatar={item.profile.avatar}
|
|
||||||
/>
|
|
||||||
<View style={[a.flex_1]}>
|
<View style={[a.flex_1]}>
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
|
|
|
@ -12,7 +12,6 @@ import {
|
||||||
QueryClient,
|
QueryClient,
|
||||||
QueryKey,
|
QueryKey,
|
||||||
useInfiniteQuery,
|
useInfiniteQuery,
|
||||||
useQueryClient,
|
|
||||||
} from '@tanstack/react-query'
|
} from '@tanstack/react-query'
|
||||||
|
|
||||||
import {HomeFeedAPI} from '#/lib/api/feed/home'
|
import {HomeFeedAPI} from '#/lib/api/feed/home'
|
||||||
|
@ -33,7 +32,6 @@ import {BSKY_FEED_OWNER_DIDS} from 'lib/constants'
|
||||||
import {KnownError} from '#/view/com/posts/FeedErrorMessage'
|
import {KnownError} from '#/view/com/posts/FeedErrorMessage'
|
||||||
import {useFeedTuners} from '../preferences/feed-tuners'
|
import {useFeedTuners} from '../preferences/feed-tuners'
|
||||||
import {useModerationOpts} from './preferences'
|
import {useModerationOpts} from './preferences'
|
||||||
import {precacheFeedPostProfiles} from './profile'
|
|
||||||
import {embedViewRecordToPostView, getEmbeddedPost} from './util'
|
import {embedViewRecordToPostView, getEmbeddedPost} from './util'
|
||||||
|
|
||||||
type ActorDid = string
|
type ActorDid = string
|
||||||
|
@ -102,7 +100,6 @@ export function usePostFeedQuery(
|
||||||
params?: FeedParams,
|
params?: FeedParams,
|
||||||
opts?: {enabled?: boolean; ignoreFilterFor?: string},
|
opts?: {enabled?: boolean; ignoreFilterFor?: string},
|
||||||
) {
|
) {
|
||||||
const queryClient = useQueryClient()
|
|
||||||
const feedTuners = useFeedTuners(feedDesc)
|
const feedTuners = useFeedTuners(feedDesc)
|
||||||
const moderationOpts = useModerationOpts()
|
const moderationOpts = useModerationOpts()
|
||||||
const {getAgent} = useAgent()
|
const {getAgent} = useAgent()
|
||||||
|
@ -151,7 +148,6 @@ export function usePostFeedQuery(
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await api.fetch({cursor, limit: PAGE_SIZE})
|
const res = await api.fetch({cursor, limit: PAGE_SIZE})
|
||||||
precacheFeedPostProfiles(queryClient, res.feed)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If this is a public view, we need to check if posts fail moderation.
|
* If this is a public view, we need to check if posts fail moderation.
|
||||||
|
|
|
@ -11,7 +11,6 @@ import {useAgent} from '#/state/session'
|
||||||
import {findAllPostsInQueryData as findAllPostsInSearchQueryData} from 'state/queries/search-posts'
|
import {findAllPostsInQueryData as findAllPostsInSearchQueryData} from 'state/queries/search-posts'
|
||||||
import {findAllPostsInQueryData as findAllPostsInNotifsQueryData} from './notifications/feed'
|
import {findAllPostsInQueryData as findAllPostsInNotifsQueryData} from './notifications/feed'
|
||||||
import {findAllPostsInQueryData as findAllPostsInFeedQueryData} from './post-feed'
|
import {findAllPostsInQueryData as findAllPostsInFeedQueryData} from './post-feed'
|
||||||
import {precacheThreadPostProfiles} from './profile'
|
|
||||||
import {embedViewRecordToPostView, getEmbeddedPost} from './util'
|
import {embedViewRecordToPostView, getEmbeddedPost} from './util'
|
||||||
|
|
||||||
const RQKEY_ROOT = 'post-thread'
|
const RQKEY_ROOT = 'post-thread'
|
||||||
|
@ -73,9 +72,7 @@ export function usePostThreadQuery(uri: string | undefined) {
|
||||||
async queryFn() {
|
async queryFn() {
|
||||||
const res = await getAgent().getPostThread({uri: uri!})
|
const res = await getAgent().getPostThread({uri: uri!})
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
const nodes = responseToThreadNodes(res.data.thread)
|
return responseToThreadNodes(res.data.thread)
|
||||||
precacheThreadPostProfiles(queryClient, nodes)
|
|
||||||
return nodes
|
|
||||||
}
|
}
|
||||||
return {type: 'unknown', uri: uri!}
|
return {type: 'unknown', uri: uri!}
|
||||||
},
|
},
|
||||||
|
|
|
@ -4,9 +4,6 @@ import {
|
||||||
AppBskyActorDefs,
|
AppBskyActorDefs,
|
||||||
AppBskyActorGetProfile,
|
AppBskyActorGetProfile,
|
||||||
AppBskyActorProfile,
|
AppBskyActorProfile,
|
||||||
AppBskyEmbedRecord,
|
|
||||||
AppBskyEmbedRecordWithMedia,
|
|
||||||
AppBskyFeedDefs,
|
|
||||||
AtUri,
|
AtUri,
|
||||||
BskyAgent,
|
BskyAgent,
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
|
@ -29,7 +26,6 @@ import {updateProfileShadow} from '../cache/profile-shadow'
|
||||||
import {useAgent, useSession} from '../session'
|
import {useAgent, useSession} from '../session'
|
||||||
import {RQKEY as RQKEY_MY_BLOCKED} from './my-blocked-accounts'
|
import {RQKEY as RQKEY_MY_BLOCKED} from './my-blocked-accounts'
|
||||||
import {RQKEY as RQKEY_MY_MUTED} from './my-muted-accounts'
|
import {RQKEY as RQKEY_MY_MUTED} from './my-muted-accounts'
|
||||||
import {ThreadNode} from './post-thread'
|
|
||||||
|
|
||||||
const RQKEY_ROOT = 'profile'
|
const RQKEY_ROOT = 'profile'
|
||||||
export const RQKEY = (did: string) => [RQKEY_ROOT, did]
|
export const RQKEY = (did: string) => [RQKEY_ROOT, did]
|
||||||
|
@ -477,56 +473,6 @@ export function precacheProfile(
|
||||||
queryClient.setQueryData(profileBasicQueryKey(profile.did), profile)
|
queryClient.setQueryData(profileBasicQueryKey(profile.did), profile)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function precacheFeedPostProfiles(
|
|
||||||
queryClient: QueryClient,
|
|
||||||
posts: AppBskyFeedDefs.FeedViewPost[],
|
|
||||||
) {
|
|
||||||
for (const post of posts) {
|
|
||||||
// Save the author of the post every time
|
|
||||||
precacheProfile(queryClient, post.post.author)
|
|
||||||
precachePostEmbedProfile(queryClient, post.post.embed)
|
|
||||||
|
|
||||||
// Cache parent author and embeds
|
|
||||||
const parent = post.reply?.parent
|
|
||||||
if (AppBskyFeedDefs.isPostView(parent)) {
|
|
||||||
precacheProfile(queryClient, parent.author)
|
|
||||||
precachePostEmbedProfile(queryClient, parent.embed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function precachePostEmbedProfile(
|
|
||||||
queryClient: QueryClient,
|
|
||||||
embed: AppBskyFeedDefs.PostView['embed'],
|
|
||||||
) {
|
|
||||||
if (AppBskyEmbedRecord.isView(embed)) {
|
|
||||||
if (AppBskyEmbedRecord.isViewRecord(embed.record)) {
|
|
||||||
precacheProfile(queryClient, embed.record.author)
|
|
||||||
}
|
|
||||||
} else if (AppBskyEmbedRecordWithMedia.isView(embed)) {
|
|
||||||
if (AppBskyEmbedRecord.isViewRecord(embed.record.record)) {
|
|
||||||
precacheProfile(queryClient, embed.record.record.author)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function precacheThreadPostProfiles(
|
|
||||||
queryClient: QueryClient,
|
|
||||||
node: ThreadNode,
|
|
||||||
) {
|
|
||||||
if (node.type === 'post') {
|
|
||||||
precacheProfile(queryClient, node.post.author)
|
|
||||||
if (node.parent) {
|
|
||||||
precacheThreadPostProfiles(queryClient, node.parent)
|
|
||||||
}
|
|
||||||
if (node.replies?.length) {
|
|
||||||
for (const reply of node.replies) {
|
|
||||||
precacheThreadPostProfiles(queryClient, reply)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function whenAppViewReady(
|
async function whenAppViewReady(
|
||||||
getAgent: () => BskyAgent,
|
getAgent: () => BskyAgent,
|
||||||
actor: string,
|
actor: string,
|
||||||
|
|
|
@ -86,9 +86,7 @@ export function ComposerReplyTo({replyTo}: {replyTo: ComposerOptsPostRef}) {
|
||||||
)}>
|
)}>
|
||||||
<PreviewableUserAvatar
|
<PreviewableUserAvatar
|
||||||
size={50}
|
size={50}
|
||||||
did={replyTo.author.did}
|
profile={replyTo.author}
|
||||||
handle={replyTo.author.handle}
|
|
||||||
avatar={replyTo.author.avatar}
|
|
||||||
moderation={replyTo.moderation?.ui('avatar')}
|
moderation={replyTo.moderation?.ui('avatar')}
|
||||||
type={replyTo.author.associated?.labeler ? 'labeler' : 'user'}
|
type={replyTo.author.associated?.labeler ? 'labeler' : 'user'}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -24,6 +24,7 @@ import {
|
||||||
} from '@fortawesome/react-native-fontawesome'
|
} from '@fortawesome/react-native-fontawesome'
|
||||||
import {msg, Trans} from '@lingui/macro'
|
import {msg, Trans} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
|
import {useQueryClient} from '@tanstack/react-query'
|
||||||
|
|
||||||
import {FeedNotification} from '#/state/queries/notifications/feed'
|
import {FeedNotification} from '#/state/queries/notifications/feed'
|
||||||
import {useAnimatedValue} from 'lib/hooks/useAnimatedValue'
|
import {useAnimatedValue} from 'lib/hooks/useAnimatedValue'
|
||||||
|
@ -36,6 +37,7 @@ import {pluralize} from 'lib/strings/helpers'
|
||||||
import {niceDate} from 'lib/strings/time'
|
import {niceDate} from 'lib/strings/time'
|
||||||
import {colors, s} from 'lib/styles'
|
import {colors, s} from 'lib/styles'
|
||||||
import {isWeb} from 'platform/detection'
|
import {isWeb} from 'platform/detection'
|
||||||
|
import {precacheProfile} from 'state/queries/profile'
|
||||||
import {Link as NewLink} from '#/components/Link'
|
import {Link as NewLink} from '#/components/Link'
|
||||||
import {ProfileHoverCard} from '#/components/ProfileHoverCard'
|
import {ProfileHoverCard} from '#/components/ProfileHoverCard'
|
||||||
import {FeedSourceCard} from '../feeds/FeedSourceCard'
|
import {FeedSourceCard} from '../feeds/FeedSourceCard'
|
||||||
|
@ -52,13 +54,9 @@ const MAX_AUTHORS = 5
|
||||||
const EXPANDED_AUTHOR_EL_HEIGHT = 35
|
const EXPANDED_AUTHOR_EL_HEIGHT = 35
|
||||||
|
|
||||||
interface Author {
|
interface Author {
|
||||||
|
profile: AppBskyActorDefs.ProfileViewBasic
|
||||||
href: string
|
href: string
|
||||||
did: string
|
|
||||||
handle: string
|
|
||||||
displayName?: string
|
|
||||||
avatar?: string
|
|
||||||
moderation: ModerationDecision
|
moderation: ModerationDecision
|
||||||
associated?: AppBskyActorDefs.ProfileAssociated
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let FeedItem = ({
|
let FeedItem = ({
|
||||||
|
@ -68,6 +66,7 @@ let FeedItem = ({
|
||||||
item: FeedNotification
|
item: FeedNotification
|
||||||
moderationOpts: ModerationOpts
|
moderationOpts: ModerationOpts
|
||||||
}): React.ReactNode => {
|
}): React.ReactNode => {
|
||||||
|
const queryClient = useQueryClient()
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const [isAuthorsExpanded, setAuthorsExpanded] = useState<boolean>(false)
|
const [isAuthorsExpanded, setAuthorsExpanded] = useState<boolean>(false)
|
||||||
|
@ -95,28 +94,22 @@ let FeedItem = ({
|
||||||
setAuthorsExpanded(currentlyExpanded => !currentlyExpanded)
|
setAuthorsExpanded(currentlyExpanded => !currentlyExpanded)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onBeforePress = React.useCallback(() => {
|
||||||
|
precacheProfile(queryClient, item.notification.author)
|
||||||
|
}, [queryClient, item.notification.author])
|
||||||
|
|
||||||
const authors: Author[] = useMemo(() => {
|
const authors: Author[] = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
profile: item.notification.author,
|
||||||
href: makeProfileLink(item.notification.author),
|
href: makeProfileLink(item.notification.author),
|
||||||
did: item.notification.author.did,
|
|
||||||
handle: item.notification.author.handle,
|
|
||||||
displayName: item.notification.author.displayName,
|
|
||||||
avatar: item.notification.author.avatar,
|
|
||||||
moderation: moderateProfile(item.notification.author, moderationOpts),
|
moderation: moderateProfile(item.notification.author, moderationOpts),
|
||||||
associated: item.notification.author.associated,
|
|
||||||
},
|
},
|
||||||
...(item.additional?.map(({author}) => {
|
...(item.additional?.map(({author}) => ({
|
||||||
return {
|
profile: author,
|
||||||
href: makeProfileLink(author),
|
href: makeProfileLink(author),
|
||||||
did: author.did,
|
|
||||||
handle: author.handle,
|
|
||||||
displayName: author.displayName,
|
|
||||||
avatar: author.avatar,
|
|
||||||
moderation: moderateProfile(author, moderationOpts),
|
moderation: moderateProfile(author, moderationOpts),
|
||||||
associated: author.associated,
|
})) || []),
|
||||||
}
|
|
||||||
}) || []),
|
|
||||||
]
|
]
|
||||||
}, [item, moderationOpts])
|
}, [item, moderationOpts])
|
||||||
|
|
||||||
|
@ -201,7 +194,8 @@ let FeedItem = ({
|
||||||
accessible={
|
accessible={
|
||||||
(item.type === 'post-like' && authors.length === 1) ||
|
(item.type === 'post-like' && authors.length === 1) ||
|
||||||
item.type === 'repost'
|
item.type === 'repost'
|
||||||
}>
|
}
|
||||||
|
onBeforePress={onBeforePress}>
|
||||||
<View style={styles.layoutIcon}>
|
<View style={styles.layoutIcon}>
|
||||||
{/* TODO: Prevent conditional rendering and move toward composable
|
{/* TODO: Prevent conditional rendering and move toward composable
|
||||||
notifications for clearer accessibility labeling */}
|
notifications for clearer accessibility labeling */}
|
||||||
|
@ -231,7 +225,7 @@ let FeedItem = ({
|
||||||
style={[pal.text, s.bold]}
|
style={[pal.text, s.bold]}
|
||||||
href={authors[0].href}
|
href={authors[0].href}
|
||||||
text={sanitizeDisplayName(
|
text={sanitizeDisplayName(
|
||||||
authors[0].displayName || authors[0].handle,
|
authors[0].profile.displayName || authors[0].profile.handle,
|
||||||
)}
|
)}
|
||||||
disableMismatchWarning
|
disableMismatchWarning
|
||||||
/>
|
/>
|
||||||
|
@ -339,11 +333,9 @@ function CondensedAuthorsList({
|
||||||
<View style={styles.avis}>
|
<View style={styles.avis}>
|
||||||
<PreviewableUserAvatar
|
<PreviewableUserAvatar
|
||||||
size={35}
|
size={35}
|
||||||
did={authors[0].did}
|
profile={authors[0].profile}
|
||||||
handle={authors[0].handle}
|
|
||||||
avatar={authors[0].avatar}
|
|
||||||
moderation={authors[0].moderation.ui('avatar')}
|
moderation={authors[0].moderation.ui('avatar')}
|
||||||
type={authors[0].associated?.labeler ? 'labeler' : 'user'}
|
type={authors[0].profile.associated?.labeler ? 'labeler' : 'user'}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
@ -360,11 +352,9 @@ function CondensedAuthorsList({
|
||||||
<View key={author.href} style={s.mr5}>
|
<View key={author.href} style={s.mr5}>
|
||||||
<PreviewableUserAvatar
|
<PreviewableUserAvatar
|
||||||
size={35}
|
size={35}
|
||||||
did={author.did}
|
profile={author.profile}
|
||||||
handle={author.handle}
|
|
||||||
avatar={author.avatar}
|
|
||||||
moderation={author.moderation.ui('avatar')}
|
moderation={author.moderation.ui('avatar')}
|
||||||
type={author.associated?.labeler ? 'labeler' : 'user'}
|
type={author.profile.associated?.labeler ? 'labeler' : 'user'}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
))}
|
))}
|
||||||
|
@ -415,20 +405,20 @@ function ExpandedAuthorsList({
|
||||||
]}>
|
]}>
|
||||||
{authors.map(author => (
|
{authors.map(author => (
|
||||||
<NewLink
|
<NewLink
|
||||||
key={author.did}
|
key={author.profile.did}
|
||||||
label={_(msg`See profile`)}
|
label={_(msg`See profile`)}
|
||||||
to={makeProfileLink({
|
to={makeProfileLink({
|
||||||
did: author.did,
|
did: author.profile.did,
|
||||||
handle: author.handle,
|
handle: author.profile.handle,
|
||||||
})}
|
})}
|
||||||
style={styles.expandedAuthor}>
|
style={styles.expandedAuthor}>
|
||||||
<View style={styles.expandedAuthorAvi}>
|
<View style={styles.expandedAuthorAvi}>
|
||||||
<ProfileHoverCard did={author.did}>
|
<ProfileHoverCard did={author.profile.did}>
|
||||||
<UserAvatar
|
<UserAvatar
|
||||||
size={35}
|
size={35}
|
||||||
avatar={author.avatar}
|
avatar={author.profile.avatar}
|
||||||
moderation={author.moderation.ui('avatar')}
|
moderation={author.moderation.ui('avatar')}
|
||||||
type={author.associated?.labeler ? 'labeler' : 'user'}
|
type={author.profile.associated?.labeler ? 'labeler' : 'user'}
|
||||||
/>
|
/>
|
||||||
</ProfileHoverCard>
|
</ProfileHoverCard>
|
||||||
</View>
|
</View>
|
||||||
|
@ -438,10 +428,12 @@ function ExpandedAuthorsList({
|
||||||
numberOfLines={1}
|
numberOfLines={1}
|
||||||
style={pal.text}
|
style={pal.text}
|
||||||
lineHeight={1.2}>
|
lineHeight={1.2}>
|
||||||
{sanitizeDisplayName(author.displayName || author.handle)}
|
{sanitizeDisplayName(
|
||||||
|
author.profile.displayName || author.profile.handle,
|
||||||
|
)}
|
||||||
|
|
||||||
<Text style={[pal.textLight]} lineHeight={1.2}>
|
<Text style={[pal.textLight]} lineHeight={1.2}>
|
||||||
{sanitizeHandle(author.handle)}
|
{sanitizeHandle(author.profile.handle)}
|
||||||
</Text>
|
</Text>
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -249,9 +249,7 @@ let PostThreadItemLoaded = ({
|
||||||
<View style={[styles.layoutAvi, {paddingBottom: 8}]}>
|
<View style={[styles.layoutAvi, {paddingBottom: 8}]}>
|
||||||
<PreviewableUserAvatar
|
<PreviewableUserAvatar
|
||||||
size={42}
|
size={42}
|
||||||
did={post.author.did}
|
profile={post.author}
|
||||||
handle={post.author.handle}
|
|
||||||
avatar={post.author.avatar}
|
|
||||||
moderation={moderation.ui('avatar')}
|
moderation={moderation.ui('avatar')}
|
||||||
type={post.author.associated?.labeler ? 'labeler' : 'user'}
|
type={post.author.associated?.labeler ? 'labeler' : 'user'}
|
||||||
/>
|
/>
|
||||||
|
@ -399,7 +397,8 @@ let PostThreadItemLoaded = ({
|
||||||
isThreadedChild
|
isThreadedChild
|
||||||
? {marginRight: 4}
|
? {marginRight: 4}
|
||||||
: {marginLeft: 2, marginRight: 2}
|
: {marginLeft: 2, marginRight: 2}
|
||||||
}>
|
}
|
||||||
|
profile={post.author}>
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
|
@ -440,9 +439,7 @@ let PostThreadItemLoaded = ({
|
||||||
<View style={styles.layoutAvi}>
|
<View style={styles.layoutAvi}>
|
||||||
<PreviewableUserAvatar
|
<PreviewableUserAvatar
|
||||||
size={38}
|
size={38}
|
||||||
did={post.author.did}
|
profile={post.author}
|
||||||
handle={post.author.handle}
|
|
||||||
avatar={post.author.avatar}
|
|
||||||
moderation={moderation.ui('avatar')}
|
moderation={moderation.ui('avatar')}
|
||||||
type={post.author.associated?.labeler ? 'labeler' : 'user'}
|
type={post.author.associated?.labeler ? 'labeler' : 'user'}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -21,7 +21,7 @@ import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {makeProfileLink} from 'lib/routes/links'
|
import {makeProfileLink} from 'lib/routes/links'
|
||||||
import {countLines} from 'lib/strings/helpers'
|
import {countLines} from 'lib/strings/helpers'
|
||||||
import {colors, s} from 'lib/styles'
|
import {colors, s} from 'lib/styles'
|
||||||
import {RQKEY as RQKEY_URI} from 'state/queries/resolve-uri'
|
import {precacheProfile} from 'state/queries/profile'
|
||||||
import {atoms as a} from '#/alf'
|
import {atoms as a} from '#/alf'
|
||||||
import {ProfileHoverCard} from '#/components/ProfileHoverCard'
|
import {ProfileHoverCard} from '#/components/ProfileHoverCard'
|
||||||
import {RichText} from '#/components/RichText'
|
import {RichText} from '#/components/RichText'
|
||||||
|
@ -135,8 +135,8 @@ function PostInner({
|
||||||
}, [setLimitLines])
|
}, [setLimitLines])
|
||||||
|
|
||||||
const onBeforePress = React.useCallback(() => {
|
const onBeforePress = React.useCallback(() => {
|
||||||
queryClient.setQueryData(RQKEY_URI(post.author.handle), post.author.did)
|
precacheProfile(queryClient, post.author)
|
||||||
}, [queryClient, post.author.handle, post.author.did])
|
}, [queryClient, post.author])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
|
@ -148,9 +148,7 @@ function PostInner({
|
||||||
<View style={styles.layoutAvi}>
|
<View style={styles.layoutAvi}>
|
||||||
<PreviewableUserAvatar
|
<PreviewableUserAvatar
|
||||||
size={52}
|
size={52}
|
||||||
did={post.author.did}
|
profile={post.author}
|
||||||
handle={post.author.handle}
|
|
||||||
avatar={post.author.avatar}
|
|
||||||
moderation={moderation.ui('avatar')}
|
moderation={moderation.ui('avatar')}
|
||||||
type={post.author.associated?.labeler ? 'labeler' : 'user'}
|
type={post.author.associated?.labeler ? 'labeler' : 'user'}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -13,6 +13,7 @@ import {
|
||||||
} from '@fortawesome/react-native-fontawesome'
|
} from '@fortawesome/react-native-fontawesome'
|
||||||
import {msg, Trans} from '@lingui/macro'
|
import {msg, Trans} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
|
import {useQueryClient} from '@tanstack/react-query'
|
||||||
|
|
||||||
import {POST_TOMBSTONE, Shadow, usePostShadow} from '#/state/cache/post-shadow'
|
import {POST_TOMBSTONE, Shadow, usePostShadow} from '#/state/cache/post-shadow'
|
||||||
import {useComposerControls} from '#/state/shell/composer'
|
import {useComposerControls} from '#/state/shell/composer'
|
||||||
|
@ -24,6 +25,7 @@ import {sanitizeDisplayName} from 'lib/strings/display-names'
|
||||||
import {sanitizeHandle} from 'lib/strings/handles'
|
import {sanitizeHandle} from 'lib/strings/handles'
|
||||||
import {countLines} from 'lib/strings/helpers'
|
import {countLines} from 'lib/strings/helpers'
|
||||||
import {s} from 'lib/styles'
|
import {s} from 'lib/styles'
|
||||||
|
import {precacheProfile} from 'state/queries/profile'
|
||||||
import {atoms as a} from '#/alf'
|
import {atoms as a} from '#/alf'
|
||||||
import {ContentHider} from '#/components/moderation/ContentHider'
|
import {ContentHider} from '#/components/moderation/ContentHider'
|
||||||
import {ProfileHoverCard} from '#/components/ProfileHoverCard'
|
import {ProfileHoverCard} from '#/components/ProfileHoverCard'
|
||||||
|
@ -106,6 +108,7 @@ let FeedItemInner = ({
|
||||||
isThreadLastChild?: boolean
|
isThreadLastChild?: boolean
|
||||||
isThreadParent?: boolean
|
isThreadParent?: boolean
|
||||||
}): React.ReactNode => {
|
}): React.ReactNode => {
|
||||||
|
const queryClient = useQueryClient()
|
||||||
const {openComposer} = useComposerControls()
|
const {openComposer} = useComposerControls()
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
|
@ -135,6 +138,10 @@ let FeedItemInner = ({
|
||||||
})
|
})
|
||||||
}, [post, record, openComposer, moderation])
|
}, [post, record, openComposer, moderation])
|
||||||
|
|
||||||
|
const onBeforePress = React.useCallback(() => {
|
||||||
|
precacheProfile(queryClient, post.author)
|
||||||
|
}, [queryClient, post.author])
|
||||||
|
|
||||||
const outerStyles = [
|
const outerStyles = [
|
||||||
styles.outer,
|
styles.outer,
|
||||||
{
|
{
|
||||||
|
@ -153,7 +160,8 @@ let FeedItemInner = ({
|
||||||
style={outerStyles}
|
style={outerStyles}
|
||||||
href={href}
|
href={href}
|
||||||
noFeedback
|
noFeedback
|
||||||
accessible={false}>
|
accessible={false}
|
||||||
|
onBeforePress={onBeforePress}>
|
||||||
<View style={{flexDirection: 'row', gap: 10, paddingLeft: 8}}>
|
<View style={{flexDirection: 'row', gap: 10, paddingLeft: 8}}>
|
||||||
<View style={{width: 52}}>
|
<View style={{width: 52}}>
|
||||||
{isThreadChild && (
|
{isThreadChild && (
|
||||||
|
@ -240,9 +248,7 @@ let FeedItemInner = ({
|
||||||
<View style={styles.layoutAvi}>
|
<View style={styles.layoutAvi}>
|
||||||
<PreviewableUserAvatar
|
<PreviewableUserAvatar
|
||||||
size={52}
|
size={52}
|
||||||
did={post.author.did}
|
profile={post.author}
|
||||||
handle={post.author.handle}
|
|
||||||
avatar={post.author.avatar}
|
|
||||||
moderation={moderation.ui('avatar')}
|
moderation={moderation.ui('avatar')}
|
||||||
type={post.author.associated?.labeler ? 'labeler' : 'user'}
|
type={post.author.associated?.labeler ? 'labeler' : 'user'}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -20,8 +20,7 @@ import {makeProfileLink} from 'lib/routes/links'
|
||||||
import {sanitizeDisplayName} from 'lib/strings/display-names'
|
import {sanitizeDisplayName} from 'lib/strings/display-names'
|
||||||
import {sanitizeHandle} from 'lib/strings/handles'
|
import {sanitizeHandle} from 'lib/strings/handles'
|
||||||
import {s} from 'lib/styles'
|
import {s} from 'lib/styles'
|
||||||
import {profileBasicQueryKey as RQKEY_PROFILE_BASIC} from 'state/queries/profile'
|
import {precacheProfile} from 'state/queries/profile'
|
||||||
import {RQKEY as RQKEY_URI} from 'state/queries/resolve-uri'
|
|
||||||
import {Link} from '../util/Link'
|
import {Link} from '../util/Link'
|
||||||
import {Text} from '../util/text/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {PreviewableUserAvatar} from '../util/UserAvatar'
|
import {PreviewableUserAvatar} from '../util/UserAvatar'
|
||||||
|
@ -58,9 +57,7 @@ export function ProfileCard({
|
||||||
|
|
||||||
const onBeforePress = React.useCallback(() => {
|
const onBeforePress = React.useCallback(() => {
|
||||||
onPress?.()
|
onPress?.()
|
||||||
|
precacheProfile(queryClient, profile)
|
||||||
queryClient.setQueryData(RQKEY_URI(profile.handle), profile.did)
|
|
||||||
queryClient.setQueryData(RQKEY_PROFILE_BASIC(profile.did), profile)
|
|
||||||
}, [onPress, profile, queryClient])
|
}, [onPress, profile, queryClient])
|
||||||
|
|
||||||
if (!moderationOpts) {
|
if (!moderationOpts) {
|
||||||
|
@ -91,9 +88,7 @@ export function ProfileCard({
|
||||||
<View style={styles.layoutAvi}>
|
<View style={styles.layoutAvi}>
|
||||||
<PreviewableUserAvatar
|
<PreviewableUserAvatar
|
||||||
size={40}
|
size={40}
|
||||||
did={profile.did}
|
profile={profile}
|
||||||
handle={profile.handle}
|
|
||||||
avatar={profile.avatar}
|
|
||||||
moderation={moderation.ui('avatar')}
|
moderation={moderation.ui('avatar')}
|
||||||
type={isLabeler ? 'labeler' : 'user'}
|
type={isLabeler ? 'labeler' : 'user'}
|
||||||
/>
|
/>
|
||||||
|
@ -238,9 +233,7 @@ function FollowersList({
|
||||||
<View style={[styles.followedByAvi, pal.view]}>
|
<View style={[styles.followedByAvi, pal.view]}>
|
||||||
<PreviewableUserAvatar
|
<PreviewableUserAvatar
|
||||||
size={32}
|
size={32}
|
||||||
did={f.did}
|
profile={f}
|
||||||
handle={f.handle}
|
|
||||||
avatar={f.avatar}
|
|
||||||
moderation={mod.ui('avatar')}
|
moderation={mod.ui('avatar')}
|
||||||
type={f.associated?.labeler ? 'labeler' : 'user'}
|
type={f.associated?.labeler ? 'labeler' : 'user'}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -220,8 +220,7 @@ function SuggestedFollow({
|
||||||
]}>
|
]}>
|
||||||
<PreviewableUserAvatar
|
<PreviewableUserAvatar
|
||||||
size={60}
|
size={60}
|
||||||
did={profile.did}
|
profile={profile}
|
||||||
handle={profile.handle}
|
|
||||||
avatar={profile.avatar}
|
avatar={profile.avatar}
|
||||||
moderation={moderation.ui('avatar')}
|
moderation={moderation.ui('avatar')}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -148,6 +148,7 @@ export const TextLink = memo(function TextLink({
|
||||||
dataSet,
|
dataSet,
|
||||||
title,
|
title,
|
||||||
onPress,
|
onPress,
|
||||||
|
onBeforePress,
|
||||||
disableMismatchWarning,
|
disableMismatchWarning,
|
||||||
navigationAction,
|
navigationAction,
|
||||||
anchorNoUnderline,
|
anchorNoUnderline,
|
||||||
|
@ -165,6 +166,7 @@ export const TextLink = memo(function TextLink({
|
||||||
disableMismatchWarning?: boolean
|
disableMismatchWarning?: boolean
|
||||||
navigationAction?: 'push' | 'replace' | 'navigate'
|
navigationAction?: 'push' | 'replace' | 'navigate'
|
||||||
anchorNoUnderline?: boolean
|
anchorNoUnderline?: boolean
|
||||||
|
onBeforePress?: () => void
|
||||||
} & TextProps) {
|
} & TextProps) {
|
||||||
const {...props} = useLinkProps({to: sanitizeUrl(href)})
|
const {...props} = useLinkProps({to: sanitizeUrl(href)})
|
||||||
const navigation = useNavigationDeduped()
|
const navigation = useNavigationDeduped()
|
||||||
|
@ -202,6 +204,7 @@ export const TextLink = memo(function TextLink({
|
||||||
// Let the browser handle opening in new tab etc.
|
// Let the browser handle opening in new tab etc.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
onBeforePress?.()
|
||||||
if (onPress) {
|
if (onPress) {
|
||||||
e?.preventDefault?.()
|
e?.preventDefault?.()
|
||||||
// @ts-ignore function signature differs by platform -prf
|
// @ts-ignore function signature differs by platform -prf
|
||||||
|
@ -226,6 +229,7 @@ export const TextLink = memo(function TextLink({
|
||||||
disableMismatchWarning,
|
disableMismatchWarning,
|
||||||
navigationAction,
|
navigationAction,
|
||||||
openLink,
|
openLink,
|
||||||
|
onBeforePress,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
const hrefAttrs = useMemo(() => {
|
const hrefAttrs = useMemo(() => {
|
||||||
|
@ -274,6 +278,7 @@ interface TextLinkOnWebOnlyProps extends TextProps {
|
||||||
title?: string
|
title?: string
|
||||||
navigationAction?: 'push' | 'replace' | 'navigate'
|
navigationAction?: 'push' | 'replace' | 'navigate'
|
||||||
disableMismatchWarning?: boolean
|
disableMismatchWarning?: boolean
|
||||||
|
onBeforePress?: () => void
|
||||||
onPointerEnter?: () => void
|
onPointerEnter?: () => void
|
||||||
anchorNoUnderline?: boolean
|
anchorNoUnderline?: boolean
|
||||||
}
|
}
|
||||||
|
@ -287,6 +292,7 @@ export const TextLinkOnWebOnly = memo(function DesktopWebTextLink({
|
||||||
lineHeight,
|
lineHeight,
|
||||||
navigationAction,
|
navigationAction,
|
||||||
disableMismatchWarning,
|
disableMismatchWarning,
|
||||||
|
onBeforePress,
|
||||||
...props
|
...props
|
||||||
}: TextLinkOnWebOnlyProps) {
|
}: TextLinkOnWebOnlyProps) {
|
||||||
if (isWeb) {
|
if (isWeb) {
|
||||||
|
@ -302,6 +308,7 @@ export const TextLinkOnWebOnly = memo(function DesktopWebTextLink({
|
||||||
title={props.title}
|
title={props.title}
|
||||||
navigationAction={navigationAction}
|
navigationAction={navigationAction}
|
||||||
disableMismatchWarning={disableMismatchWarning}
|
disableMismatchWarning={disableMismatchWarning}
|
||||||
|
onBeforePress={onBeforePress}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import React, {memo} from 'react'
|
import React, {memo, useCallback} from 'react'
|
||||||
import {StyleProp, StyleSheet, TextStyle, View, ViewStyle} from 'react-native'
|
import {StyleProp, StyleSheet, TextStyle, View, ViewStyle} from 'react-native'
|
||||||
import {AppBskyActorDefs, ModerationDecision, ModerationUI} from '@atproto/api'
|
import {AppBskyActorDefs, ModerationDecision, ModerationUI} from '@atproto/api'
|
||||||
|
import {useQueryClient} from '@tanstack/react-query'
|
||||||
|
|
||||||
import {usePrefetchProfileQuery} from '#/state/queries/profile'
|
import {precacheProfile, usePrefetchProfileQuery} from '#/state/queries/profile'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {makeProfileLink} from 'lib/routes/links'
|
import {makeProfileLink} from 'lib/routes/links'
|
||||||
import {sanitizeDisplayName} from 'lib/strings/display-names'
|
import {sanitizeDisplayName} from 'lib/strings/display-names'
|
||||||
|
@ -40,15 +41,18 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => {
|
||||||
? () => prefetchProfileQuery(opts.author.did)
|
? () => prefetchProfileQuery(opts.author.did)
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
const onBeforePress = useCallback(() => {
|
||||||
|
precacheProfile(queryClient, opts.author)
|
||||||
|
}, [queryClient, opts.author])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.container, opts.style]}>
|
<View style={[styles.container, opts.style]}>
|
||||||
{opts.showAvatar && (
|
{opts.showAvatar && (
|
||||||
<View style={styles.avatar}>
|
<View style={styles.avatar}>
|
||||||
<PreviewableUserAvatar
|
<PreviewableUserAvatar
|
||||||
size={opts.avatarSize || 16}
|
size={opts.avatarSize || 16}
|
||||||
did={opts.author.did}
|
profile={opts.author}
|
||||||
handle={opts.author.handle}
|
|
||||||
avatar={opts.author.avatar}
|
|
||||||
moderation={opts.avatarModeration}
|
moderation={opts.avatarModeration}
|
||||||
type={opts.author.associated?.labeler ? 'labeler' : 'user'}
|
type={opts.author.associated?.labeler ? 'labeler' : 'user'}
|
||||||
/>
|
/>
|
||||||
|
@ -71,6 +75,7 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => {
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
href={profileLink}
|
href={profileLink}
|
||||||
|
onBeforePress={onBeforePress}
|
||||||
onPointerEnter={onPointerEnter}
|
onPointerEnter={onPointerEnter}
|
||||||
/>
|
/>
|
||||||
<TextLinkOnWebOnly
|
<TextLinkOnWebOnly
|
||||||
|
@ -79,6 +84,7 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => {
|
||||||
style={[pal.textLight, {flexShrink: 4}]}
|
style={[pal.textLight, {flexShrink: 4}]}
|
||||||
text={'\xa0' + sanitizeHandle(handle, '@')}
|
text={'\xa0' + sanitizeHandle(handle, '@')}
|
||||||
href={profileLink}
|
href={profileLink}
|
||||||
|
onBeforePress={onBeforePress}
|
||||||
onPointerEnter={onPointerEnter}
|
onPointerEnter={onPointerEnter}
|
||||||
anchorNoUnderline
|
anchorNoUnderline
|
||||||
/>
|
/>
|
||||||
|
@ -103,6 +109,7 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => {
|
||||||
title={niceDate(opts.timestamp)}
|
title={niceDate(opts.timestamp)}
|
||||||
accessibilityHint=""
|
accessibilityHint=""
|
||||||
href={opts.postHref}
|
href={opts.postHref}
|
||||||
|
onBeforePress={onBeforePress}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</TimeElapsed>
|
</TimeElapsed>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {ago} from 'lib/strings/time'
|
|
||||||
import {useTickEveryMinute} from '#/state/shell'
|
import {useTickEveryMinute} from '#/state/shell'
|
||||||
|
import {ago} from 'lib/strings/time'
|
||||||
|
|
||||||
// FIXME(dan): Figure out why the false positives
|
// FIXME(dan): Figure out why the false positives
|
||||||
|
|
||||||
|
@ -12,7 +13,7 @@ export function TimeElapsed({
|
||||||
children: ({timeElapsed}: {timeElapsed: string}) => JSX.Element
|
children: ({timeElapsed}: {timeElapsed: string}) => JSX.Element
|
||||||
}) {
|
}) {
|
||||||
const tick = useTickEveryMinute()
|
const tick = useTickEveryMinute()
|
||||||
const [timeElapsed, setTimeAgo] = React.useState(ago(timestamp))
|
const [timeElapsed, setTimeAgo] = React.useState(() => ago(timestamp))
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
setTimeAgo(ago(timestamp))
|
setTimeAgo(ago(timestamp))
|
||||||
|
|
|
@ -2,10 +2,11 @@ import React, {memo, useMemo} from 'react'
|
||||||
import {Image, StyleSheet, TouchableOpacity, View} from 'react-native'
|
import {Image, StyleSheet, TouchableOpacity, View} from 'react-native'
|
||||||
import {Image as RNImage} from 'react-native-image-crop-picker'
|
import {Image as RNImage} from 'react-native-image-crop-picker'
|
||||||
import Svg, {Circle, Path, Rect} from 'react-native-svg'
|
import Svg, {Circle, Path, Rect} from 'react-native-svg'
|
||||||
import {ModerationUI} from '@atproto/api'
|
import {AppBskyActorDefs, ModerationUI} from '@atproto/api'
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {msg, Trans} from '@lingui/macro'
|
import {msg, Trans} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
|
import {useQueryClient} from '@tanstack/react-query'
|
||||||
|
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {
|
import {
|
||||||
|
@ -15,6 +16,7 @@ import {
|
||||||
import {makeProfileLink} from 'lib/routes/links'
|
import {makeProfileLink} from 'lib/routes/links'
|
||||||
import {colors} from 'lib/styles'
|
import {colors} from 'lib/styles'
|
||||||
import {isAndroid, isNative, isWeb} from 'platform/detection'
|
import {isAndroid, isNative, isWeb} from 'platform/detection'
|
||||||
|
import {precacheProfile} from 'state/queries/profile'
|
||||||
import {HighPriorityImage} from 'view/com/util/images/Image'
|
import {HighPriorityImage} from 'view/com/util/images/Image'
|
||||||
import {tokens, useTheme} from '#/alf'
|
import {tokens, useTheme} from '#/alf'
|
||||||
import {
|
import {
|
||||||
|
@ -47,8 +49,7 @@ interface EditableUserAvatarProps extends BaseUserAvatarProps {
|
||||||
|
|
||||||
interface PreviewableUserAvatarProps extends BaseUserAvatarProps {
|
interface PreviewableUserAvatarProps extends BaseUserAvatarProps {
|
||||||
moderation?: ModerationUI
|
moderation?: ModerationUI
|
||||||
did: string
|
profile: AppBskyActorDefs.ProfileViewBasic
|
||||||
handle: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const BLUR_AMOUNT = isWeb ? 5 : 100
|
const BLUR_AMOUNT = isWeb ? 5 : 100
|
||||||
|
@ -371,19 +372,28 @@ let EditableUserAvatar = ({
|
||||||
EditableUserAvatar = memo(EditableUserAvatar)
|
EditableUserAvatar = memo(EditableUserAvatar)
|
||||||
export {EditableUserAvatar}
|
export {EditableUserAvatar}
|
||||||
|
|
||||||
let PreviewableUserAvatar = (
|
let PreviewableUserAvatar = ({
|
||||||
props: PreviewableUserAvatarProps,
|
moderation,
|
||||||
): React.ReactNode => {
|
profile,
|
||||||
|
...rest
|
||||||
|
}: PreviewableUserAvatarProps): React.ReactNode => {
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
|
const onPress = React.useCallback(() => {
|
||||||
|
precacheProfile(queryClient, profile)
|
||||||
|
}, [profile, queryClient])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ProfileHoverCard did={props.did}>
|
<ProfileHoverCard did={profile.did}>
|
||||||
<Link
|
<Link
|
||||||
label={_(msg`See profile`)}
|
label={_(msg`See profile`)}
|
||||||
to={makeProfileLink({
|
to={makeProfileLink({
|
||||||
did: props.did,
|
did: profile.did,
|
||||||
handle: props.handle,
|
handle: profile.handle,
|
||||||
})}>
|
})}
|
||||||
<UserAvatar {...props} />
|
onPress={onPress}>
|
||||||
|
<UserAvatar avatar={profile.avatar} moderation={moderation} {...rest} />
|
||||||
</Link>
|
</Link>
|
||||||
</ProfileHoverCard>
|
</ProfileHoverCard>
|
||||||
)
|
)
|
||||||
|
|
|
@ -26,10 +26,10 @@ import {useQueryClient} from '@tanstack/react-query'
|
||||||
import {HITSLOP_20} from '#/lib/constants'
|
import {HITSLOP_20} from '#/lib/constants'
|
||||||
import {s} from '#/lib/styles'
|
import {s} from '#/lib/styles'
|
||||||
import {useModerationOpts} from '#/state/queries/preferences'
|
import {useModerationOpts} from '#/state/queries/preferences'
|
||||||
import {RQKEY as RQKEY_URI} from '#/state/queries/resolve-uri'
|
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {InfoCircleIcon} from 'lib/icons'
|
import {InfoCircleIcon} from 'lib/icons'
|
||||||
import {makeProfileLink} from 'lib/routes/links'
|
import {makeProfileLink} from 'lib/routes/links'
|
||||||
|
import {precacheProfile} from 'state/queries/profile'
|
||||||
import {ComposerOptsQuote} from 'state/shell/composer'
|
import {ComposerOptsQuote} from 'state/shell/composer'
|
||||||
import {atoms as a} from '#/alf'
|
import {atoms as a} from '#/alf'
|
||||||
import {RichText} from '#/components/RichText'
|
import {RichText} from '#/components/RichText'
|
||||||
|
@ -149,8 +149,8 @@ export function QuoteEmbed({
|
||||||
}, [quote.embeds])
|
}, [quote.embeds])
|
||||||
|
|
||||||
const onBeforePress = React.useCallback(() => {
|
const onBeforePress = React.useCallback(() => {
|
||||||
queryClient.setQueryData(RQKEY_URI(quote.author.handle), quote.author.did)
|
precacheProfile(queryClient, quote.author)
|
||||||
}, [queryClient, quote.author.did, quote.author.handle])
|
}, [queryClient, quote.author])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ContentHider modui={moderation?.ui('contentList')}>
|
<ContentHider modui={moderation?.ui('contentList')}>
|
||||||
|
|
Loading…
Reference in New Issue