Fix DM embeds being dropped when sending (#10924)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -37,13 +37,14 @@ import {PaperPlaneVertical_Filled_Stroke2_Corner1_Rounded as PaperPlaneIcon} fro
|
||||
import {Loader} from '#/components/Loader'
|
||||
import * as Toast from '#/components/Toast'
|
||||
import {IS_ANDROID, IS_IOS, IS_LIQUID_GLASS, IS_NATIVE, IS_WEB} from '#/env'
|
||||
import {type MessageEmbedState} from './MessageInputEmbed'
|
||||
|
||||
const MIN_HEIGHT = 40
|
||||
|
||||
export function MessageComposer({
|
||||
textInputId,
|
||||
onSendMessage,
|
||||
hasEmbed,
|
||||
messageEmbed,
|
||||
setEmbed,
|
||||
children,
|
||||
loading = false,
|
||||
@@ -51,9 +52,10 @@ export function MessageComposer({
|
||||
textInputId?: string
|
||||
onSendMessage: (
|
||||
message: string,
|
||||
embed?: MessageEmbedState,
|
||||
replyTo?: $Typed<ChatBskyConvoDefs.MessageView>,
|
||||
) => void
|
||||
hasEmbed: boolean
|
||||
messageEmbed: MessageEmbedState | undefined
|
||||
setEmbed: (embedUrl: string | undefined) => void
|
||||
children?: React.ReactNode
|
||||
loading?: boolean
|
||||
@@ -89,14 +91,16 @@ export function MessageComposer({
|
||||
},
|
||||
})
|
||||
|
||||
const submitDisabled = !editable || (!hasEmbed && text.trim().length === 0)
|
||||
const submitDisabled =
|
||||
!editable || (!messageEmbed && text.trim().length === 0)
|
||||
|
||||
const onSubmit = (
|
||||
message: string,
|
||||
embed: MessageEmbedState | undefined,
|
||||
replyTo: ChatBskyConvoDefs.MessageView | null,
|
||||
) => {
|
||||
if (!editable) return
|
||||
if (!hasEmbed && message.trim() === '') return
|
||||
if (!embed && message.trim() === '') return
|
||||
const graphemeCount = countGraphemes(message)
|
||||
if (graphemeCount > MAX_DM_GRAPHEME_LENGTH) {
|
||||
Toast.show(
|
||||
@@ -120,6 +124,7 @@ export function MessageComposer({
|
||||
requestAnimationFrame(() => {
|
||||
onSendMessage(
|
||||
message,
|
||||
embed,
|
||||
replyTo
|
||||
? {
|
||||
...replyTo,
|
||||
@@ -152,18 +157,18 @@ export function MessageComposer({
|
||||
setTimeout(() => {
|
||||
if (isFlushingAutocorrectSuggestion.current) {
|
||||
isFlushingAutocorrectSuggestion.current = false
|
||||
onSubmit(text, replyTo)
|
||||
onSubmit(text, messageEmbed, replyTo)
|
||||
}
|
||||
}, 20)
|
||||
} else {
|
||||
onSubmit(text, replyTo)
|
||||
onSubmit(text, messageEmbed, replyTo)
|
||||
}
|
||||
}
|
||||
|
||||
const handleChange = (nextText: string) => {
|
||||
if (IS_IOS && isFlushingAutocorrectSuggestion.current) {
|
||||
isFlushingAutocorrectSuggestion.current = false
|
||||
onSubmit(nextText, replyTo)
|
||||
onSubmit(nextText, messageEmbed, replyTo)
|
||||
} else {
|
||||
setText(nextText)
|
||||
}
|
||||
|
||||
@@ -34,7 +34,10 @@ import {Loader} from '#/components/Loader'
|
||||
import * as Toast from '#/components/Toast'
|
||||
import {IS_ANDROID, IS_IOS, IS_WEB} from '#/env'
|
||||
import {ComposerContainer} from './MessageComposer'
|
||||
import {useExtractEmbedFromFacets} from './MessageInputEmbed'
|
||||
import {
|
||||
type MessageEmbedState,
|
||||
useExtractEmbedFromFacets,
|
||||
} from './MessageInputEmbed'
|
||||
|
||||
const AnimatedTextInput = Animated.createAnimatedComponent(TextInput)
|
||||
|
||||
@@ -43,7 +46,7 @@ const MIN_HEIGHT = 40
|
||||
export function MessageInput({
|
||||
textInputId,
|
||||
onSendMessage,
|
||||
hasEmbed,
|
||||
messageEmbed,
|
||||
setEmbed,
|
||||
children,
|
||||
loading = false,
|
||||
@@ -51,9 +54,10 @@ export function MessageInput({
|
||||
textInputId?: string
|
||||
onSendMessage: (
|
||||
message: string,
|
||||
embed?: MessageEmbedState,
|
||||
replyTo?: $Typed<ChatBskyConvoDefs.MessageView>,
|
||||
) => Promise<void> | void
|
||||
hasEmbed: boolean
|
||||
messageEmbed: MessageEmbedState | undefined
|
||||
setEmbed: (embedUrl: string | undefined) => void
|
||||
children?: React.ReactNode
|
||||
loading?: boolean
|
||||
@@ -85,7 +89,7 @@ export function MessageInput({
|
||||
if (!editable) {
|
||||
return
|
||||
}
|
||||
if (!hasEmbed && message.trim() === '') {
|
||||
if (!messageEmbed && message.trim() === '') {
|
||||
return
|
||||
}
|
||||
if (countGraphemes(message) > MAX_DM_GRAPHEME_LENGTH) {
|
||||
@@ -96,6 +100,8 @@ export function MessageInput({
|
||||
}
|
||||
clearDraft()
|
||||
playHaptic()
|
||||
// Capture the embed before clearing - the deferred send below reads it.
|
||||
const embed = messageEmbed
|
||||
setEmbed(undefined)
|
||||
setMessage('')
|
||||
// Capture the reply before clearing - the deferred send below reads it.
|
||||
@@ -115,6 +121,7 @@ export function MessageInput({
|
||||
requestAnimationFrame(() => {
|
||||
void onSendMessage(
|
||||
message,
|
||||
embed,
|
||||
reply
|
||||
? {...reply, $type: 'chat.bsky.convo.defs#messageView'}
|
||||
: undefined,
|
||||
@@ -122,7 +129,7 @@ export function MessageInput({
|
||||
})
|
||||
}, [
|
||||
editable,
|
||||
hasEmbed,
|
||||
messageEmbed,
|
||||
message,
|
||||
clearDraft,
|
||||
onSendMessage,
|
||||
@@ -159,7 +166,8 @@ export function MessageInput({
|
||||
scrollEnabled: isInputScrollable.get(),
|
||||
}))
|
||||
|
||||
const submitDisabled = !editable || (!hasEmbed && message.trim().length === 0)
|
||||
const submitDisabled =
|
||||
!editable || (!messageEmbed && message.trim().length === 0)
|
||||
|
||||
const blur = useCallback(() => {
|
||||
inputRef.current?.blur()
|
||||
|
||||
@@ -21,20 +21,24 @@ import {EmojiArc_Stroke2_Corner0_Rounded as EmojiSmile} from '#/components/icons
|
||||
import {PaperPlane_Stroke2_Corner0_Rounded as PaperPlane} from '#/components/icons/PaperPlane'
|
||||
import * as Toast from '#/components/Toast'
|
||||
import {IS_WEB_SAFARI, IS_WEB_TOUCH_DEVICE} from '#/env'
|
||||
import {useExtractEmbedFromFacets} from './MessageInputEmbed'
|
||||
import {
|
||||
type MessageEmbedState,
|
||||
useExtractEmbedFromFacets,
|
||||
} from './MessageInputEmbed'
|
||||
|
||||
export function MessageInput({
|
||||
onSendMessage,
|
||||
hasEmbed,
|
||||
messageEmbed,
|
||||
setEmbed,
|
||||
children,
|
||||
loading = false,
|
||||
}: {
|
||||
onSendMessage: (
|
||||
message: string,
|
||||
embed?: MessageEmbedState,
|
||||
replyTo?: $Typed<ChatBskyConvoDefs.MessageView>,
|
||||
) => void
|
||||
hasEmbed: boolean
|
||||
messageEmbed: MessageEmbedState | undefined
|
||||
setEmbed: (embedUrl: string | undefined) => void
|
||||
children?: React.ReactNode
|
||||
loading?: boolean
|
||||
@@ -54,7 +58,7 @@ export function MessageInput({
|
||||
const textAreaRef = useRef<HTMLTextAreaElement>(null)
|
||||
|
||||
const onSubmit = useCallback(() => {
|
||||
if (!hasEmbed && message.trim() === '') {
|
||||
if (!messageEmbed && message.trim() === '') {
|
||||
return
|
||||
}
|
||||
if (countGraphemes(message) > MAX_DM_GRAPHEME_LENGTH) {
|
||||
@@ -66,6 +70,7 @@ export function MessageInput({
|
||||
clearDraft()
|
||||
onSendMessage(
|
||||
message,
|
||||
messageEmbed,
|
||||
replyTo
|
||||
? {...replyTo, $type: 'chat.bsky.convo.defs#messageView'}
|
||||
: undefined,
|
||||
@@ -78,7 +83,7 @@ export function MessageInput({
|
||||
onSendMessage,
|
||||
l,
|
||||
clearDraft,
|
||||
hasEmbed,
|
||||
messageEmbed,
|
||||
setEmbed,
|
||||
replyTo,
|
||||
clearReply,
|
||||
|
||||
@@ -378,7 +378,11 @@ export function MessagesList({
|
||||
|
||||
// -- Message sending
|
||||
const onSendMessage = useCallback(
|
||||
async (text: string, reply?: $Typed<ChatBskyConvoDefs.MessageView>) => {
|
||||
async (
|
||||
text: string,
|
||||
embedState?: MessageEmbedState,
|
||||
reply?: $Typed<ChatBskyConvoDefs.MessageView>,
|
||||
) => {
|
||||
let rt = new RichText({text: text.trimEnd()}, {cleanNewlines: true})
|
||||
|
||||
// detect facets without resolution first - this is used to see if there's
|
||||
@@ -416,9 +420,9 @@ export function MessagesList({
|
||||
}
|
||||
}
|
||||
|
||||
if (messageEmbed?.type === 'post') {
|
||||
if (embedState?.type === 'post') {
|
||||
try {
|
||||
const post = await getPost({uri: messageEmbed.uri})
|
||||
const post = await getPost({uri: embedState.uri})
|
||||
if (post) {
|
||||
embed = {
|
||||
$type: 'app.bsky.embed.record',
|
||||
@@ -445,8 +449,8 @@ export function MessagesList({
|
||||
} catch (error) {
|
||||
logger.error('Failed to get post as quote for DM', {error})
|
||||
}
|
||||
} else if (messageEmbed?.type === 'invite') {
|
||||
const code = messageEmbed.code
|
||||
} else if (embedState?.type === 'invite') {
|
||||
const code = embedState.code
|
||||
embed = {
|
||||
$type: 'chat.bsky.embed.joinLink',
|
||||
code,
|
||||
@@ -512,7 +516,6 @@ export function MessagesList({
|
||||
[
|
||||
agent,
|
||||
convoState,
|
||||
messageEmbed,
|
||||
getPost,
|
||||
getJoinLinkPreview,
|
||||
hasSession,
|
||||
@@ -737,6 +740,7 @@ function Composer({
|
||||
textInputId: string
|
||||
onSendMessage: (
|
||||
message: string,
|
||||
embed?: MessageEmbedState,
|
||||
replyTo?: $Typed<ChatBskyConvoDefs.MessageView>,
|
||||
) => Promise<void>
|
||||
messageEmbed: MessageEmbedState | undefined
|
||||
@@ -745,8 +749,12 @@ function Composer({
|
||||
useNewComposer: boolean
|
||||
}) {
|
||||
const handleSendMessage = useNonReactiveCallback(
|
||||
(message: string, replyTo?: $Typed<ChatBskyConvoDefs.MessageView>) => {
|
||||
void onSendMessage(message, replyTo)
|
||||
(
|
||||
message: string,
|
||||
embed?: MessageEmbedState,
|
||||
replyTo?: $Typed<ChatBskyConvoDefs.MessageView>,
|
||||
) => {
|
||||
void onSendMessage(message, embed, replyTo)
|
||||
},
|
||||
)
|
||||
|
||||
@@ -761,7 +769,7 @@ function Composer({
|
||||
<MessageComposer
|
||||
textInputId={textInputId}
|
||||
onSendMessage={handleSendMessage}
|
||||
hasEmbed={!!messageEmbed}
|
||||
messageEmbed={messageEmbed}
|
||||
setEmbed={setEmbed}
|
||||
loading={loading}>
|
||||
{previews}
|
||||
@@ -770,7 +778,7 @@ function Composer({
|
||||
<MessageInput
|
||||
textInputId={textInputId}
|
||||
onSendMessage={handleSendMessage}
|
||||
hasEmbed={!!messageEmbed}
|
||||
messageEmbed={messageEmbed}
|
||||
setEmbed={setEmbed}
|
||||
loading={loading}>
|
||||
{previews}
|
||||
|
||||
Reference in New Issue
Block a user