diff --git a/assets/icons/arrowCornerDownRight_stroke2_rounded_2_rounded.svg b/assets/icons/arrowCornerDownRight_stroke2_rounded_2_rounded.svg
new file mode 100644
index 0000000000..fa21c9824e
--- /dev/null
+++ b/assets/icons/arrowCornerDownRight_stroke2_rounded_2_rounded.svg
@@ -0,0 +1 @@
+
diff --git a/src/components/Post/PostRepliedTo.tsx b/src/components/Post/PostRepliedTo.tsx
new file mode 100644
index 0000000000..3085826c23
--- /dev/null
+++ b/src/components/Post/PostRepliedTo.tsx
@@ -0,0 +1,63 @@
+import {View} from 'react-native'
+import {Trans} from '@lingui/macro'
+
+import {useSession} from '#/state/session'
+import {UserInfoText} from '#/view/com/util/UserInfoText'
+import {atoms as a, useTheme} from '#/alf'
+import {ArrowCornerDownRight_Stroke2_Corner2_Rounded as ArrowCornerDownRightIcon} from '#/components/icons/ArrowCornerDownRight'
+import {ProfileHoverCard} from '#/components/ProfileHoverCard'
+import {Text} from '#/components/Typography'
+import type * as bsky from '#/types/bsky'
+
+export function PostRepliedTo({
+ parentAuthor,
+ isParentBlocked,
+ isParentNotFound,
+}: {
+ parentAuthor: string | bsky.profile.AnyProfileView | undefined
+ isParentBlocked?: boolean
+ isParentNotFound?: boolean
+}) {
+ const t = useTheme()
+ const {currentAccount} = useSession()
+
+ const textStyle = [a.text_sm, t.atoms.text_contrast_medium, a.leading_snug]
+
+ let label
+ if (isParentBlocked) {
+ label = Replied to a blocked post
+ } else if (isParentNotFound) {
+ label = Replied to a post
+ } else if (parentAuthor) {
+ const did =
+ typeof parentAuthor === 'string' ? parentAuthor : parentAuthor.did
+ const isMe = currentAccount?.did === did
+ if (isMe) {
+ label = Replied to you
+ } else {
+ label = (
+
+ Replied to{' '}
+
+
+
+
+ )
+ }
+ }
+
+ if (!label) {
+ // Should not happen.
+ return null
+ }
+
+ return (
+
+
+ {label}
+
+ )
+}
diff --git a/src/components/icons/ArrowCornerDownRight.tsx b/src/components/icons/ArrowCornerDownRight.tsx
new file mode 100644
index 0000000000..86dde70154
--- /dev/null
+++ b/src/components/icons/ArrowCornerDownRight.tsx
@@ -0,0 +1,7 @@
+import {createSinglePathSVG} from './TEMPLATE'
+
+export const ArrowCornerDownRight_Stroke2_Corner2_Rounded = createSinglePathSVG(
+ {
+ path: 'M15.793 10.293a1 1 0 0 1 1.338-.068l.076.068 3.293 3.293a2 2 0 0 1 .138 2.677l-.138.151-3.293 3.293a1 1 0 1 1-1.414-1.414L18.086 16H8a5 5 0 0 1-5-5V5a1 1 0 0 1 2 0v6a3 3 0 0 0 3 3h10.086l-2.293-2.293-.068-.076a1 1 0 0 1 .068-1.338Z',
+ },
+)
diff --git a/src/view/com/post/Post.tsx b/src/view/com/post/Post.tsx
index 6079f5c101..a8e32268e1 100644
--- a/src/view/com/post/Post.tsx
+++ b/src/view/com/post/Post.tsx
@@ -8,8 +8,6 @@ import {
type ModerationDecision,
RichText as RichTextAPI,
} from '@atproto/api'
-import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
-import {Trans} from '@lingui/macro'
import {useQueryClient} from '@tanstack/react-query'
import {MAX_POST_LINES} from '#/lib/constants'
@@ -17,28 +15,25 @@ import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
import {usePalette} from '#/lib/hooks/usePalette'
import {makeProfileLink} from '#/lib/routes/links'
import {countLines} from '#/lib/strings/helpers'
-import {colors, s} from '#/lib/styles'
+import {colors} from '#/lib/styles'
import {
POST_TOMBSTONE,
type Shadow,
usePostShadow,
} from '#/state/cache/post-shadow'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
-import {precacheProfile} from '#/state/queries/profile'
-import {useSession} from '#/state/session'
+import {unstableCacheProfileView} from '#/state/queries/profile'
import {Link} from '#/view/com/util/Link'
import {PostMeta} from '#/view/com/util/PostMeta'
-import {Text} from '#/view/com/util/text/Text'
import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar'
-import {UserInfoText} from '#/view/com/util/UserInfoText'
import {atoms as a} from '#/alf'
import {ContentHider} from '#/components/moderation/ContentHider'
import {LabelsOnMyPost} from '#/components/moderation/LabelsOnMe'
import {PostAlerts} from '#/components/moderation/PostAlerts'
import {Embed, PostEmbedViewContext} from '#/components/Post/Embed'
+import {PostRepliedTo} from '#/components/Post/PostRepliedTo'
import {ShowMoreTextButton} from '#/components/Post/ShowMoreTextButton'
import {PostControls} from '#/components/PostControls'
-import {ProfileHoverCard} from '#/components/ProfileHoverCard'
import {RichText} from '#/components/RichText'
import {SubtleWebHover} from '#/components/SubtleWebHover'
import * as bsky from '#/types/bsky'
@@ -145,12 +140,9 @@ function PostInner({
}, [setLimitLines])
const onBeforePress = useCallback(() => {
- precacheProfile(queryClient, post.author)
+ unstableCacheProfileView(queryClient, post.author)
}, [queryClient, post.author])
- const {currentAccount} = useSession()
- const isMe = replyAuthorDid === currentAccount?.did
-
const [hover, setHover] = useState(false)
return (
{replyAuthorDid !== '' && (
-
-
-
- {isMe ? (
- Reply to you
- ) : (
-
- Reply to{' '}
-
-
-
-
- )}
-
-
+
)}
{showReplyTo &&
(parentAuthor || isParentBlocked || isParentNotFound) && (
-
)}
@@ -576,80 +572,10 @@ let PostContent = ({
}
PostContent = memo(PostContent)
-function ReplyToLabel({
- profile,
- blocked,
- notFound,
-}: {
- profile: AppBskyActorDefs.ProfileViewBasic | undefined
- blocked?: boolean
- notFound?: boolean
-}) {
- const pal = usePalette('default')
- const {currentAccount} = useSession()
-
- let label
- if (blocked) {
- label = Reply to a blocked post
- } else if (notFound) {
- label = Reply to a post
- } else if (profile != null) {
- const isMe = profile.did === currentAccount?.did
- if (isMe) {
- label = Reply to you
- } else {
- label = (
-
- Reply to{' '}
-
-
- {profile.displayName
- ? sanitizeDisplayName(profile.displayName)
- : sanitizeHandle(profile.handle)}
-
- }
- />
-
-
- )
- }
- }
-
- if (!label) {
- // Should not happen.
- return null
- }
-
- return (
-
-
-
- {label}
-
-
- )
-}
-
const styles = StyleSheet.create({
outer: {
paddingLeft: 10,
paddingRight: 15,
- // @ts-ignore web only -prf
cursor: 'pointer',
},
replyLine: {
diff --git a/src/view/com/util/UserInfoText.tsx b/src/view/com/util/UserInfoText.tsx
index 64aa37ff2c..028b85d38c 100644
--- a/src/view/com/util/UserInfoText.tsx
+++ b/src/view/com/util/UserInfoText.tsx
@@ -1,27 +1,25 @@
-import {StyleProp, StyleSheet, TextStyle} from 'react-native'
-import {AppBskyActorGetProfile as GetProfile} from '@atproto/api'
+import {type StyleProp, type TextStyle} from 'react-native'
+import {type AppBskyActorGetProfile} from '@atproto/api'
import {makeProfileLink} from '#/lib/routes/links'
import {sanitizeDisplayName} from '#/lib/strings/display-names'
import {sanitizeHandle} from '#/lib/strings/handles'
-import {TypographyVariant} from '#/lib/ThemeContext'
import {STALE} from '#/state/queries'
import {useProfileQuery} from '#/state/queries/profile'
-import {TextLinkOnWebOnly} from './Link'
+import {atoms as a} from '#/alf'
+import {InlineLinkText} from '#/components/Link'
+import {Text} from '#/components/Typography'
import {LoadingPlaceholder} from './LoadingPlaceholder'
-import {Text} from './text/Text'
export function UserInfoText({
- type = 'md',
did,
attr,
failed,
prefix,
style,
}: {
- type?: TypographyVariant
did: string
- attr?: keyof GetProfile.OutputSchema
+ attr?: keyof AppBskyActorGetProfile.OutputSchema
loading?: string
failed?: string
prefix?: string
@@ -35,45 +33,37 @@ export function UserInfoText({
staleTime: STALE.INFINITY,
})
- let inner
if (isError) {
- inner = (
-
+ return (
+
{failed}
)
} else if (profile) {
- inner = (
-
- {`${prefix || ''}${sanitizeDisplayName(
- typeof profile[attr] === 'string' && profile[attr]
- ? (profile[attr] as string)
- : sanitizeHandle(profile.handle),
- )}`}
-
- }
- />
- )
- } else {
- inner = (
-
+ to={makeProfileLink(profile)}>
+
+ {text}
+
+
)
}
- return inner
+ // eslint-disable-next-line bsky-internal/avoid-unwrapped-text
+ return (
+
+ )
}
-
-const styles = StyleSheet.create({
- loadingPlaceholder: {position: 'relative', top: 1, left: 2},
-})