diff --git a/src/components/LabelingServiceCard/index.tsx b/src/components/LabelingServiceCard/index.tsx
index 10d8c9733..7e083b78a 100644
--- a/src/components/LabelingServiceCard/index.tsx
+++ b/src/components/LabelingServiceCard/index.tsx
@@ -51,11 +51,19 @@ export function Title({value}: {value: string}) {
)
}
-export function Description({value, handle}: {value?: string; handle: string}) {
+export function Description({
+ value,
+ handle,
+ did,
+}: {
+ value?: string
+ handle: string
+ did: string
+}) {
const {_} = useLingui()
return value ? (
-
+
) : (
@@ -139,6 +147,7 @@ export function Default({
{labeler.likeCount ? : null}
diff --git a/src/components/Post/Embed/index.tsx b/src/components/Post/Embed/index.tsx
index 4919e5005..324a60ff8 100644
--- a/src/components/Post/Embed/index.tsx
+++ b/src/components/Post/Embed/index.tsx
@@ -429,6 +429,7 @@ export function QuoteEmbed({
style={a.text_md}
numberOfLines={20}
disableLinks
+ authorDid={quote.author.did}
/>
) : null}
{quote.embed && (
diff --git a/src/components/ProfileCard.tsx b/src/components/ProfileCard.tsx
index d470222ca..fa724356e 100644
--- a/src/components/ProfileCard.tsx
+++ b/src/components/ProfileCard.tsx
@@ -429,6 +429,7 @@ export function Description({
style={style}
numberOfLines={numberOfLines}
disableLinks
+ authorDid={profile.did}
/>
)
diff --git a/src/components/ProfileHoverCard/index.web.tsx b/src/components/ProfileHoverCard/index.web.tsx
index e49e7da17..c3678664d 100644
--- a/src/components/ProfileHoverCard/index.web.tsx
+++ b/src/components/ProfileHoverCard/index.web.tsx
@@ -748,6 +748,7 @@ function Inner({
numberOfLines={8}
value={descriptionRT}
onLinkPress={hide}
+ authorDid={profile.did}
/>
) : undefined}
diff --git a/src/components/RichText.tsx b/src/components/RichText.tsx
index 0dd348a71..c9531826e 100644
--- a/src/components/RichText.tsx
+++ b/src/components/RichText.tsx
@@ -1,8 +1,18 @@
import {useMemo} from 'react'
import {type StyleProp, type TextStyle} from 'react-native'
-import {AppBskyRichtextFacet, RichText as RichTextAPI} from '@atproto/api'
+import {Image as ExpoImage} from 'expo-image'
+import {
+ AppBskyRichtextFacet,
+ type BlobRef,
+ type ComAtprotoRepoStrongRef,
+ RichText as RichTextAPI,
+} from '@atproto/api'
+import {useLingui} from '@lingui/react/macro'
import {toShortUrl} from '#/lib/strings/url-helpers'
+import {useImageCdnHost} from '#/state/preferences'
+import {applyImageTransforms} from '#/state/preferences/image-cdn-host'
+import { useThumbnailFormat } from '#/state/preferences/thumbnail-format'
import {atoms as a, flatten, type TextStyleProp} from '#/alf'
import {isOnlyEmoji} from '#/alf/typography'
import {InlineLinkText, type LinkProps} from '#/components/Link'
@@ -23,6 +33,7 @@ export type RichTextProps = TextStyleProp &
disableLinks?: boolean
enableTags?: boolean
authorHandle?: string
+ authorDid?: string
onLinkPress?: LinkProps['onPress']
interactiveStyle?: StyleProp
emojiMultiplier?: number
@@ -39,6 +50,18 @@ export type RichTextProps = TextStyleProp &
disableMentionFacetValidation?: true
}
+type EmojiFeature = {
+ name: string
+ alt: string
+ emoji: ComAtprotoRepoStrongRef.Main
+ image: BlobRef
+}
+
+// This is a bit silly, but it works
+function makeCdnUri(did: string, cid: string) {
+ return `https://cdn.bsky.app/img/avatar_thumbnail/plain/${did}/${cid}@png`
+}
+
export function RichText({
testID,
value,
@@ -55,7 +78,11 @@ export function RichText({
onTextLayout,
shouldProxyLinks,
disableMentionFacetValidation,
+ authorDid,
}: RichTextProps) {
+ const {t: l} = useLingui()
+ const imageCdnHost = useImageCdnHost()
+ const thumbnailFormat = useThumbnailFormat()
const richText = useMemo(() => {
if (value instanceof RichTextAPI) {
return value
@@ -113,6 +140,10 @@ export function RichText({
const link = segment.link
const mention = segment.mention
const tag = segment.tag
+ const userEmoji = segment.facet?.features.find(
+ feature =>
+ feature.$type === 'dev.maelstrom.bsky.richtext.facet#userEmoji',
+ ) as EmojiFeature | undefined
if (
mention &&
@@ -155,6 +186,29 @@ export function RichText({
,
)
}
+ } else if (userEmoji && authorDid) {
+ els.push(
+ // TODO(maelstrom): This uses inline-block, which is not compatible with mobile
+ ,
+ )
} else if (
!disableLinks &&
enableTags &&
diff --git a/src/components/dms/MessageItem.tsx b/src/components/dms/MessageItem.tsx
index 9d1b449f5..5fb20207c 100644
--- a/src/components/dms/MessageItem.tsx
+++ b/src/components/dms/MessageItem.tsx
@@ -484,6 +484,7 @@ let MessageItem = ({
enableTags
emojiMultiplier={3}
shouldProxyLinks={true}
+ authorDid={profile?.did}
/>
)}
diff --git a/src/screens/Messages/components/MessageInputEmbed.tsx b/src/screens/Messages/components/MessageInputEmbed.tsx
index 4b69e1f67..74b38007b 100644
--- a/src/screens/Messages/components/MessageInputEmbed.tsx
+++ b/src/screens/Messages/components/MessageInputEmbed.tsx
@@ -251,6 +251,7 @@ function MessageInputPostEmbed({
value={rt}
style={[a.text_sm, t.atoms.text_contrast_high]}
authorHandle={post.author.handle}
+ authorDid={post.author.did}
numberOfLines={3}
/>
)}
diff --git a/src/screens/PostThread/components/ThreadItemAnchor.tsx b/src/screens/PostThread/components/ThreadItemAnchor.tsx
index d4e35dc9a..f31b6dd3b 100644
--- a/src/screens/PostThread/components/ThreadItemAnchor.tsx
+++ b/src/screens/PostThread/components/ThreadItemAnchor.tsx
@@ -469,6 +469,7 @@ const ThreadItemAnchorInner = memo(function ThreadItemAnchorInner({
value={richText}
style={[a.flex_1, isCompactPosts ? a.text_md : a.text_lg]}
authorHandle={post.author.handle}
+ authorDid={post.author.did}
shouldProxyLinks={true}
/>
) : undefined}
diff --git a/src/screens/PostThread/components/ThreadItemPost.tsx b/src/screens/PostThread/components/ThreadItemPost.tsx
index 1ed8707e8..65fb80264 100644
--- a/src/screens/PostThread/components/ThreadItemPost.tsx
+++ b/src/screens/PostThread/components/ThreadItemPost.tsx
@@ -346,6 +346,7 @@ const ThreadItemPostInner = memo(function ThreadItemPostInner({
style={[a.flex_1, a.text_md]}
numberOfLines={limitLines ? MAX_POST_LINES : undefined}
authorHandle={post.author.handle}
+ authorDid={post.author.did}
shouldProxyLinks={true}
/>
{limitLines && (
diff --git a/src/screens/PostThread/components/ThreadItemTreePost.tsx b/src/screens/PostThread/components/ThreadItemTreePost.tsx
index 1faf35a12..0a2e2b656 100644
--- a/src/screens/PostThread/components/ThreadItemTreePost.tsx
+++ b/src/screens/PostThread/components/ThreadItemTreePost.tsx
@@ -355,6 +355,7 @@ const ThreadItemTreePostInner = memo(function ThreadItemTreePostInner({
style={[a.flex_1, a.text_md]}
numberOfLines={limitLines ? MAX_POST_LINES : undefined}
authorHandle={post.author.handle}
+ authorDid={post.author.did}
shouldProxyLinks={true}
/>
{limitLines && (
diff --git a/src/screens/Profile/Header/ProfileHeaderLabeler.tsx b/src/screens/Profile/Header/ProfileHeaderLabeler.tsx
index cb491575b..de7fe5664 100644
--- a/src/screens/Profile/Header/ProfileHeaderLabeler.tsx
+++ b/src/screens/Profile/Header/ProfileHeaderLabeler.tsx
@@ -139,6 +139,7 @@ let ProfileHeaderLabeler = ({
value={descriptionRT}
enableTags
authorHandle={profile.handle}
+ authorDid={profile.did}
/>
) : undefined}
diff --git a/src/screens/Profile/Header/ProfileHeaderStandard.tsx b/src/screens/Profile/Header/ProfileHeaderStandard.tsx
index c67db6aeb..e5cd905a6 100644
--- a/src/screens/Profile/Header/ProfileHeaderStandard.tsx
+++ b/src/screens/Profile/Header/ProfileHeaderStandard.tsx
@@ -238,6 +238,7 @@ let ProfileHeaderStandard = ({
value={descriptionRT}
enableTags
authorHandle={profile.handle}
+ authorDid={profile.did}
/>
) : undefined}
diff --git a/src/screens/VideoFeed/index.tsx b/src/screens/VideoFeed/index.tsx
index 549a481e2..9a5d30325 100644
--- a/src/screens/VideoFeed/index.tsx
+++ b/src/screens/VideoFeed/index.tsx
@@ -980,15 +980,53 @@ function Overlay({
) : (
Follow
)}
-
-
- )
- )}
+
+ {profile.viewer?.following ? (
+ Following
+ ) : (
+ Follow
+ )}
+
+
+ )}
+ />
+ ) : (
+
+ ))}
{record?.text?.trim() && (
)}
{record && (
@@ -1058,9 +1096,11 @@ function Overlay({
function ExpandableRichTextView({
value,
authorHandle,
+ authorDid,
}: {
value: RichTextAPI
authorHandle?: string
+ authorDid?: string
}) {
const {height: screenHeight} = useSafeAreaFrame()
const [expanded, setExpanded] = useState(false)
@@ -1096,6 +1136,7 @@ function ExpandableRichTextView({
value={value}
style={[a.text_sm, a.flex_1, a.leading_relaxed]}
authorHandle={authorHandle}
+ authorDid={authorDid}
enableTags
numberOfLines={
expanded || screenReaderEnabled ? undefined : constrained ? 2 : 2
diff --git a/src/view/com/post/Post.tsx b/src/view/com/post/Post.tsx
index 3233f8d6d..248313987 100644
--- a/src/view/com/post/Post.tsx
+++ b/src/view/com/post/Post.tsx
@@ -239,6 +239,7 @@ function PostInner({
numberOfLines={limitLines ? MAX_POST_LINES : undefined}
style={[a.flex_1, a.text_md]}
authorHandle={post.author.handle}
+ authorDid={post.author.did}
shouldProxyLinks={true}
/>
{limitLines && (
diff --git a/src/view/com/posts/PostFeedItem.tsx b/src/view/com/posts/PostFeedItem.tsx
index 42460ebfb..084641e82 100644
--- a/src/view/com/posts/PostFeedItem.tsx
+++ b/src/view/com/posts/PostFeedItem.tsx
@@ -532,6 +532,7 @@ let PostContent = ({
numberOfLines={limitLines ? MAX_POST_LINES : undefined}
style={[a.flex_1, a.text_md]}
authorHandle={postAuthor.handle}
+ authorDid={post.author.did}
shouldProxyLinks={true}
/>
{limitLines && (