diff --git a/src/components/dms/MessageItem.tsx b/src/components/dms/MessageItem.tsx
index c5ff810915..b498ddf1c4 100644
--- a/src/components/dms/MessageItem.tsx
+++ b/src/components/dms/MessageItem.tsx
@@ -6,7 +6,11 @@ import {
TextStyle,
View,
} from 'react-native'
-import {ChatBskyConvoDefs, RichText as RichTextAPI} from '@atproto/api'
+import {
+ AppBskyEmbedRecord,
+ ChatBskyConvoDefs,
+ RichText as RichTextAPI,
+} from '@atproto/api'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
@@ -18,6 +22,7 @@ import {ActionsWrapper} from '#/components/dms/ActionsWrapper'
import {InlineLinkText} from '#/components/Link'
import {Text} from '#/components/Typography'
import {RichText} from '../RichText'
+import {MessageItemEmbed} from './MessageItemEmbed'
let MessageItem = ({
item,
@@ -77,37 +82,44 @@ let MessageItem = ({
return (
-
-
+ )}
+ {rt.text.length > 0 && (
+
-
+ a.py_sm,
+ a.my_2xs,
+ a.rounded_md,
+ {
+ paddingLeft: 14,
+ paddingRight: 14,
+ backgroundColor: isFromSelf
+ ? isPending
+ ? pendingColor
+ : t.palette.primary_500
+ : t.palette.contrast_50,
+ borderRadius: 17,
+ },
+ isFromSelf ? a.self_end : a.self_start,
+ isFromSelf
+ ? {borderBottomRightRadius: isLastInGroup ? 2 : 17}
+ : {borderBottomLeftRadius: isLastInGroup ? 2 : 17},
+ ]}>
+
+
+ )}
{isLastInGroup && (
diff --git a/src/components/dms/MessageItemEmbed.tsx b/src/components/dms/MessageItemEmbed.tsx
new file mode 100644
index 0000000000..d64563b912
--- /dev/null
+++ b/src/components/dms/MessageItemEmbed.tsx
@@ -0,0 +1,109 @@
+import React, {useMemo} from 'react'
+import {View} from 'react-native'
+import {
+ AppBskyEmbedRecord,
+ AppBskyFeedPost,
+ AtUri,
+ RichText as RichTextAPI,
+} from '@atproto/api'
+
+import {moderatePost_wrapped as moderatePost} from '#/lib/moderatePost_wrapped'
+import {makeProfileLink} from '#/lib/routes/links'
+import {useModerationOpts} from '#/state/preferences/moderation-opts'
+import {usePostQuery} from '#/state/queries/post'
+import {PostEmbeds} from '#/view/com/util/post-embeds'
+import {PostMeta} from '#/view/com/util/PostMeta'
+import {atoms as a, useTheme} from '#/alf'
+import {Link} from '#/components/Link'
+import {ContentHider} from '#/components/moderation/ContentHider'
+import {PostAlerts} from '#/components/moderation/PostAlerts'
+import {RichText} from '#/components/RichText'
+
+let MessageItemEmbed = ({
+ embed,
+}: {
+ embed: AppBskyEmbedRecord.Main
+}): React.ReactNode => {
+ const t = useTheme()
+ const {data: post} = usePostQuery(embed.record.uri)
+
+ const moderationOpts = useModerationOpts()
+ const moderation = useMemo(
+ () =>
+ moderationOpts && post ? moderatePost(post, moderationOpts) : undefined,
+ [moderationOpts, post],
+ )
+
+ const {rt, record} = useMemo(() => {
+ if (
+ post &&
+ AppBskyFeedPost.isRecord(post.record) &&
+ AppBskyFeedPost.validateRecord(post.record).success
+ ) {
+ return {
+ rt: new RichTextAPI({
+ text: post.record.text,
+ facets: post.record.facets,
+ }),
+ record: post.record,
+ }
+ }
+
+ return {rt: undefined, record: undefined}
+ }, [post])
+
+ if (!post || !moderation || !rt || !record) {
+ return null
+ }
+
+ const itemUrip = new AtUri(post.uri)
+ const itemHref = makeProfileLink(post.author, 'post', itemUrip.rkey)
+
+ return (
+
+
+
+
+
+ {rt.text && (
+
+
+
+ )}
+ {post.embed && (
+
+ )}
+
+
+
+ )
+}
+MessageItemEmbed = React.memo(MessageItemEmbed)
+export {MessageItemEmbed}
diff --git a/src/components/dms/MessageMenu.tsx b/src/components/dms/MessageMenu.tsx
index 21812d2688..92913d1cb2 100644
--- a/src/components/dms/MessageMenu.tsx
+++ b/src/components/dms/MessageMenu.tsx
@@ -6,12 +6,16 @@ import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {richTextToString} from '#/lib/strings/rich-text-helpers'
+import {getTranslatorLink} from '#/locale/helpers'
+import {useLanguagePrefs} from '#/state/preferences'
+import {useOpenLink} from '#/state/preferences/in-app-browser'
import {isWeb} from 'platform/detection'
import {useConvoActive} from 'state/messages/convo'
import {useSession} from 'state/session'
import * as Toast from '#/view/com/util/Toast'
import {atoms as a, useTheme} from '#/alf'
import {ReportDialog} from '#/components/dms/ReportDialog'
+import {BubbleQuestion_Stroke2_Corner0_Rounded as Translate} from '#/components/icons/Bubble'
import {DotGrid_Stroke2_Corner0_Rounded as DotsHorizontal} from '#/components/icons/DotGrid'
import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash'
import {Warning_Stroke2_Corner0_Rounded as Warning} from '#/components/icons/Warning'
@@ -35,10 +39,12 @@ export let MessageMenu = ({
const convo = useConvoActive()
const deleteControl = usePromptControl()
const reportControl = usePromptControl()
+ const langPrefs = useLanguagePrefs()
+ const openLink = useOpenLink()
const isFromSelf = message.sender?.did === currentAccount?.did
- const onCopyPostText = React.useCallback(() => {
+ const onCopyMessage = React.useCallback(() => {
const str = richTextToString(
new RichText({
text: message.text,
@@ -51,6 +57,14 @@ export let MessageMenu = ({
Toast.show(_(msg`Copied to clipboard`))
}, [_, message.text, message.facets])
+ const onPressTranslateMessage = React.useCallback(() => {
+ const translatorUrl = getTranslatorLink(
+ message.text,
+ langPrefs.primaryLanguage,
+ )
+ openLink(translatorUrl)
+ }, [langPrefs.primaryLanguage, message.text, openLink])
+
const onDelete = React.useCallback(() => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
convo
@@ -81,16 +95,27 @@ export let MessageMenu = ({
)}
-
-
- {_(msg`Copy message text`)}
-
-
-
-
+ {message.text.length > 0 && (
+ <>
+
+
+ {_(msg`Translate`)}
+
+
+
+ {_(msg`Copy message text`)}
+
+
+
+
+ >
+ )}