Add loading state to message composer and input (#10595)
This commit is contained in:
@@ -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({
|
||||
<Composer
|
||||
nativeID={textInputId}
|
||||
label={l`Message input field`}
|
||||
placeholder={l`Message`}
|
||||
placeholder={
|
||||
loading
|
||||
? l({message: 'Loading chat…', context: 'placeholder'})
|
||||
: l({message: 'Message', context: 'action'})
|
||||
}
|
||||
autocompletePlacement="top-start"
|
||||
internalApiRef={composerInternalApiRef}
|
||||
defaultValue={text}
|
||||
@@ -238,7 +245,11 @@ export function MessageComposer({
|
||||
/>
|
||||
</View>
|
||||
</GlassView>
|
||||
<SubmitButton onPress={handleSubmit} disabled={submitDisabled} />
|
||||
<SubmitButton
|
||||
onPress={handleSubmit}
|
||||
disabled={submitDisabled}
|
||||
loading={loading}
|
||||
/>
|
||||
</GlassContainer>
|
||||
</View>
|
||||
</ComposerContainer>
|
||||
@@ -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}>
|
||||
<PaperPlaneIcon size="md" fill={t.palette.white} style={[a.mb_2xs]} />
|
||||
{loading ? (
|
||||
<Loader size="md" fill={t.palette.white} style={[a.mb_2xs]} />
|
||||
) : (
|
||||
<PaperPlaneIcon size="md" fill={t.palette.white} style={[a.mb_2xs]} />
|
||||
)}
|
||||
</Pressable>
|
||||
</GlassView>
|
||||
)
|
||||
|
||||
@@ -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> | 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}
|
||||
/>
|
||||
</GlassView>
|
||||
<GlassView
|
||||
@@ -226,7 +229,11 @@ export function MessageInput({
|
||||
}}>
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={l`Send message`}
|
||||
accessibilityLabel={
|
||||
loading
|
||||
? l({message: 'Loading chat…', context: 'placeholder'})
|
||||
: l({message: 'Message', context: 'action'})
|
||||
}
|
||||
accessibilityHint=""
|
||||
hitSlop={HITSLOP_10}
|
||||
style={[
|
||||
@@ -240,11 +247,15 @@ export function MessageInput({
|
||||
]}
|
||||
onPress={onSubmit}
|
||||
disabled={submitDisabled}>
|
||||
<PaperPlaneIcon
|
||||
size="md"
|
||||
fill={t.palette.white}
|
||||
style={[a.mb_2xs]}
|
||||
/>
|
||||
{loading ? (
|
||||
<Loader size="md" fill={t.palette.white} style={[a.mb_2xs]} />
|
||||
) : (
|
||||
<PaperPlaneIcon
|
||||
size="md"
|
||||
fill={t.palette.white}
|
||||
style={[a.mb_2xs]}
|
||||
/>
|
||||
)}
|
||||
</Pressable>
|
||||
</GlassView>
|
||||
</GlassContainer>
|
||||
|
||||
Reference in New Issue
Block a user