about summary refs log tree commit diff
path: root/src/screens/Messages/Conversation/MessagesList.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/screens/Messages/Conversation/MessagesList.tsx')
-rw-r--r--src/screens/Messages/Conversation/MessagesList.tsx85
1 files changed, 71 insertions, 14 deletions
diff --git a/src/screens/Messages/Conversation/MessagesList.tsx b/src/screens/Messages/Conversation/MessagesList.tsx
index 583c40852..d6aa06a1c 100644
--- a/src/screens/Messages/Conversation/MessagesList.tsx
+++ b/src/screens/Messages/Conversation/MessagesList.tsx
@@ -13,12 +13,16 @@ import {
 } from 'react-native-reanimated'
 import {ReanimatedScrollEvent} from 'react-native-reanimated/lib/typescript/reanimated2/hook/commonTypes'
 import {useSafeAreaInsets} from 'react-native-safe-area-context'
-import {AppBskyRichtextFacet, RichText} from '@atproto/api'
+import {AppBskyEmbedRecord, AppBskyRichtextFacet, RichText} from '@atproto/api'
 
-import {shortenLinks} from '#/lib/strings/rich-text-manip'
+import {getPostAsQuote} from '#/lib/link-meta/bsky'
+import {shortenLinks, stripInvalidMentions} from '#/lib/strings/rich-text-manip'
+import {isBskyPostUrl} from '#/lib/strings/url-helpers'
+import {logger} from '#/logger'
 import {isNative} from '#/platform/detection'
 import {isConvoActive, useConvoActive} from '#/state/messages/convo'
 import {ConvoItem, ConvoStatus} from '#/state/messages/convo/types'
+import {useGetPost} from '#/state/queries/post'
 import {useAgent} from '#/state/session'
 import {clamp} from 'lib/numbers'
 import {ScrollProvider} from 'lib/ScrollContext'
@@ -80,6 +84,7 @@ export function MessagesList({
 }) {
   const convoState = useConvoActive()
   const agent = useAgent()
+  const getPost = useGetPost()
 
   const flatListRef = useAnimatedRef<FlatList>()
 
@@ -264,20 +269,71 @@ export function MessagesList({
   // -- Message sending
   const onSendMessage = useCallback(
     async (text: string) => {
-      let rt = new RichText({text}, {cleanNewlines: true})
-      await rt.detectFacets(agent)
-      rt = shortenLinks(rt)
+      let rt = new RichText({text: text.trimEnd()}, {cleanNewlines: true})
+
+      // detect facets without resolution first - this is used to see if there's
+      // any post links in the text that we can embed. We do this first because
+      // we want to remove the post link from the text, re-trim, then detect facets
+      rt.detectFacetsWithoutResolution()
+
+      let embed: AppBskyEmbedRecord.Main | undefined
+      // find the first link facet that is a link to a post
+      const postLinkFacet = rt.facets?.find(facet => {
+        return facet.features.find(feature => {
+          if (AppBskyRichtextFacet.isLink(feature)) {
+            return isBskyPostUrl(feature.uri)
+          }
+          return false
+        })
+      })
 
-      // filter out any mention facets that didn't map to a user
-      rt.facets = rt.facets?.filter(facet => {
-        const mention = facet.features.find(feature =>
-          AppBskyRichtextFacet.isMention(feature),
+      // if we found a post link, get the post and embed it
+      if (postLinkFacet) {
+        const postLink = postLinkFacet.features.find(
+          AppBskyRichtextFacet.isLink,
         )
-        if (mention && !mention.did) {
-          return false
+        if (!postLink) return
+
+        try {
+          const post = await getPostAsQuote(getPost, postLink.uri)
+          if (post) {
+            embed = {
+              $type: 'app.bsky.embed.record',
+              record: {
+                uri: post.uri,
+                cid: post.cid,
+              },
+            }
+
+            // remove the post link from the text
+            rt.delete(
+              postLinkFacet.index.byteStart,
+              postLinkFacet.index.byteEnd,
+            )
+
+            // re-trim the text, now that we've removed the post link
+            //
+            // if the post link is at the start of the text, we don't want to leave a leading space
+            // so trim on both sides
+            if (postLinkFacet.index.byteStart === 0) {
+              rt = new RichText({text: rt.text.trim()}, {cleanNewlines: true})
+            } else {
+              // otherwise just trim the end
+              rt = new RichText(
+                {text: rt.text.trimEnd()},
+                {cleanNewlines: true},
+              )
+            }
+          }
+        } catch (error) {
+          logger.error('Failed to get post as quote for DM', {error})
         }
-        return true
-      })
+      }
+
+      await rt.detectFacets(agent)
+
+      rt = shortenLinks(rt)
+      rt = stripInvalidMentions(rt)
 
       if (!hasScrolled) {
         setHasScrolled(true)
@@ -286,9 +342,10 @@ export function MessagesList({
       convoState.sendMessage({
         text: rt.text,
         facets: rt.facets,
+        embed,
       })
     },
-    [convoState, agent, hasScrolled, setHasScrolled],
+    [agent, convoState, getPost, hasScrolled, setHasScrolled],
   )
 
   // -- List layout changes (opening emoji keyboard, etc.)