From 6159cd7777e7847367eff57c1c48474802230211 Mon Sep 17 00:00:00 2001
From: DS Boyce <260543580+ds-boyce@users.noreply.github.com>
Date: Fri, 22 May 2026 13:57:19 -0700
Subject: [PATCH] Add loading state to message composer and input (#10595)
---
.../Messages/components/MessageComposer.tsx | 25 +++++++++++---
.../Messages/components/MessageInput.tsx | 33 ++++++++++++-------
2 files changed, 43 insertions(+), 15 deletions(-)
diff --git a/src/screens/Messages/components/MessageComposer.tsx b/src/screens/Messages/components/MessageComposer.tsx
index 8433da2ffd..b486ceddc7 100644
--- a/src/screens/Messages/components/MessageComposer.tsx
+++ b/src/screens/Messages/components/MessageComposer.tsx
@@ -32,6 +32,7 @@ import * as EmojiPicker from '#/components/EmojiPicker'
import {GlassView} from '#/components/GlassView'
import {EmojiArc_Stroke2_Corner0_Rounded as EmojiSmileIcon} from '#/components/icons/Emoji'
import {PaperPlaneVertical_Filled_Stroke2_Corner1_Rounded as PaperPlaneIcon} from '#/components/icons/PaperPlane'
+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'
@@ -43,18 +44,20 @@ export function MessageComposer({
hasEmbed,
setEmbed,
children,
+ loading = false,
}: {
textInputId?: string
onSendMessage: (message: string) => void
hasEmbed: boolean
setEmbed: (embedUrl: string | undefined) => void
children?: React.ReactNode
+ loading?: boolean
}) {
const t = useTheme()
const {t: l} = useLingui()
const playHaptic = useHaptics()
const {needsEmailVerification} = useEmail()
- const editable = !needsEmailVerification
+ const editable = !needsEmailVerification && !loading
const {getDraft, clearDraft} = useMessageDraft()
const composerInternalApiRef = useComposerInternalApiRef()
@@ -209,7 +212,11 @@ export function MessageComposer({
-
+
@@ -248,9 +259,11 @@ export function MessageComposer({
function SubmitButton({
onPress,
disabled,
+ loading,
}: {
onPress: () => void
disabled: boolean
+ loading: boolean
}) {
const {t: l} = useLingui()
const t = useTheme()
@@ -279,7 +292,11 @@ function SubmitButton({
]}
onPress={onPress}
disabled={disabled}>
-
+ {loading ? (
+
+ ) : (
+
+ )}
)
diff --git a/src/screens/Messages/components/MessageInput.tsx b/src/screens/Messages/components/MessageInput.tsx
index f52bbbeb43..dbe716efb1 100644
--- a/src/screens/Messages/components/MessageInput.tsx
+++ b/src/screens/Messages/components/MessageInput.tsx
@@ -28,6 +28,7 @@ import {
import {atoms as a, platform, tokens, useTheme} from '#/alf'
import {GlassView} from '#/components/GlassView'
import {PaperPlaneVertical_Filled_Stroke2_Corner1_Rounded as PaperPlaneIcon} from '#/components/icons/PaperPlane'
+import {Loader} from '#/components/Loader'
import * as Toast from '#/components/Toast'
import {IS_ANDROID, IS_IOS, IS_WEB} from '#/env'
import {ComposerContainer} from './MessageComposer'
@@ -43,12 +44,14 @@ export function MessageInput({
hasEmbed,
setEmbed,
children,
+ loading = false,
}: {
textInputId?: string
onSendMessage: (message: string) => Promise | void
hasEmbed: boolean
setEmbed: (embedUrl: string | undefined) => void
children?: React.ReactNode
+ loading?: boolean
}) {
const {t: l} = useLingui()
const t = useTheme()
@@ -67,12 +70,13 @@ export function MessageInput({
const [shouldEnforceClear, setShouldEnforceClear] = useState(false)
const {needsEmailVerification} = useEmail()
+ const editable = !needsEmailVerification && !loading
useSaveMessageDraft(message)
useExtractEmbedFromFacets(message, setEmbed)
const onSubmit = useCallback(() => {
- if (needsEmailVerification) {
+ if (!editable) {
return
}
if (!hasEmbed && message.trim() === '') {
@@ -103,7 +107,7 @@ export function MessageInput({
void onSendMessage(message)
})
}, [
- needsEmailVerification,
+ editable,
hasEmbed,
message,
clearDraft,
@@ -139,8 +143,7 @@ export function MessageInput({
scrollEnabled: isInputScrollable.get(),
}))
- const submitDisabled =
- needsEmailVerification || (!hasEmbed && message.trim().length === 0)
+ const submitDisabled = !editable || (!hasEmbed && message.trim().length === 0)
const blur = useCallback(() => {
inputRef.current?.blur()
@@ -209,7 +212,7 @@ export function MessageInput({
ref={inputRef}
hitSlop={HITSLOP_10}
animatedProps={animatedProps}
- editable={!needsEmailVerification}
+ editable={editable}
/>
-
+ {loading ? (
+
+ ) : (
+
+ )}