Fix DMs input automatically accepting autocomplete suggestion instead of clearing (#7796)

* fix input not clearing when autocomplete suggestion active

* fix up storybook

* restore web focus hack
This commit is contained in:
Samuel Newman
2025-02-24 13:03:01 -08:00
committed by GitHub
parent 9c739912a3
commit 7c14b54ab2
3 changed files with 59 additions and 40 deletions
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback, useState} from 'react'
import {Pressable, TextInput, useWindowDimensions, View} from 'react-native'
import {
useFocusedInputHandler,
@@ -19,7 +19,7 @@ import Graphemer from 'graphemer'
import {HITSLOP_10, MAX_DM_GRAPHEME_LENGTH} from '#/lib/constants'
import {useHaptics} from '#/lib/haptics'
import {useEmail} from '#/lib/hooks/useEmail'
import {isIOS} from '#/platform/detection'
import {isIOS, isWeb} from '#/platform/detection'
import {
useMessageDraft,
useSaveMessageDraft,
@@ -58,16 +58,17 @@ export function MessageInput({
const isInputScrollable = useSharedValue(false)
const inputStyles = useSharedInputStyles()
const [isFocused, setIsFocused] = React.useState(false)
const [message, setMessage] = React.useState(getDraft)
const [isFocused, setIsFocused] = useState(false)
const [message, setMessage] = useState(getDraft)
const inputRef = useAnimatedRef<TextInput>()
const [shouldEnforceClear, setShouldEnforceClear] = useState(false)
const {needsEmailVerification} = useEmail()
useSaveMessageDraft(message)
useExtractEmbedFromFacets(message, setEmbed)
const onSubmit = React.useCallback(() => {
const onSubmit = useCallback(() => {
if (needsEmailVerification) {
return
}
@@ -81,14 +82,18 @@ export function MessageInput({
clearDraft()
onSendMessage(message)
playHaptic()
setMessage('')
setEmbed(undefined)
// Pressing the send button causes the text input to lose focus, so we need to
// re-focus it after sending
setTimeout(() => {
inputRef.current?.focus()
}, 100)
setMessage('')
if (isIOS) {
setShouldEnforceClear(true)
}
if (isWeb) {
// Pressing the send button causes the text input to lose focus, so we need to
// re-focus it after sending
setTimeout(() => {
inputRef.current?.focus()
}, 100)
}
}, [
needsEmailVerification,
hasEmbed,
@@ -97,8 +102,8 @@ export function MessageInput({
onSendMessage,
playHaptic,
setEmbed,
_,
inputRef,
_,
])
useFocusedInputHandler(
@@ -149,8 +154,21 @@ export function MessageInput({
placeholder={_(msg`Write a message`)}
placeholderTextColor={t.palette.contrast_500}
value={message}
onChange={evt => {
// bit of a hack: iOS automatically accepts autocomplete suggestions when you tap anywhere on the screen
// including the button we just pressed - and this overrides clearing the input! so we watch for the
// next change and double make sure the input is cleared. It should *always* send an onChange event after
// clearing via setMessage('') that happens in onSubmit()
// -sfn
if (isIOS && shouldEnforceClear) {
setShouldEnforceClear(false)
setMessage('')
return
}
const text = evt.nativeEvent.text
setMessage(text)
}}
multiline={true}
onChangeText={setMessage}
style={[
a.flex_1,
a.text_md,
@@ -160,7 +178,7 @@ export function MessageInput({
animatedStyle,
]}
keyboardAppearance={t.name === 'light' ? 'light' : 'dark'}
blurOnSubmit={false}
submitBehavior="submit"
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
ref={inputRef}
+11 -8
View File
@@ -1,5 +1,5 @@
import React from 'react'
import {View} from 'react-native'
import {TextInput, View} from 'react-native'
import {atoms as a} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
@@ -19,6 +19,8 @@ export function Forms() {
const [value, setValue] = React.useState('')
const [date, setDate] = React.useState('2001-01-01')
const inputRef = React.useRef<TextInput>(null)
return (
<View style={[a.gap_4xl, a.align_start]}>
<H1>Forms</H1>
@@ -33,22 +35,23 @@ export function Forms() {
/>
<View style={[a.flex_row, a.align_start, a.gap_sm]}>
<View
style={[
{
width: '50%',
},
]}>
<View style={[a.flex_1]}>
<TextField.Root>
<TextField.Icon icon={Globe} />
<TextField.Input
inputRef={inputRef}
value={value}
onChangeText={setValue}
label="Text field"
/>
</TextField.Root>
</View>
<Button label="Submit" size="large" variant="solid" color="primary">
<Button
label="Submit"
size="large"
variant="solid"
color="primary"
onPress={() => inputRef.current?.clear()}>
<ButtonText>Submit</ButtonText>
</Button>
</View>
+15 -17
View File
@@ -1,13 +1,11 @@
import React from 'react'
import {ScrollView, View} from 'react-native'
import {View} from 'react-native'
import {useNavigation} from '@react-navigation/native'
import {NavigationProp} from '#/lib/routes/types'
import {isWeb} from '#/platform/detection'
import {useSetThemePrefs} from '#/state/shell'
import {CenteredView} from '#/view/com/util/Views'
import {ListContained} from '#/view/screens/Storybook/ListContained'
import {atoms as a, ThemeProvider, useTheme} from '#/alf'
import {atoms as a, ThemeProvider} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
import * as Layout from '#/components/Layout'
import {Admonitions} from './Admonitions'
@@ -27,25 +25,27 @@ import {Typography} from './Typography'
export function Storybook() {
return (
<Layout.Screen>
{isWeb ? (
<Layout.Header.Outer>
<Layout.Header.BackButton />
<Layout.Header.Content>
<Layout.Header.TitleText>Storybook</Layout.Header.TitleText>
</Layout.Header.Content>
<Layout.Header.Slot />
</Layout.Header.Outer>
<Layout.Content keyboardShouldPersistTaps="handled">
<StorybookInner />
) : (
<ScrollView>
<StorybookInner />
</ScrollView>
)}
</Layout.Content>
</Layout.Screen>
)
}
function StorybookInner() {
const t = useTheme()
const {setColorMode, setDarkTheme} = useSetThemePrefs()
const [showContainedList, setShowContainedList] = React.useState(false)
const navigation = useNavigation<NavigationProp>()
return (
<CenteredView style={[t.atoms.bg]}>
<>
<View style={[a.p_xl, a.gap_5xl, {paddingBottom: 100}]}>
{!showContainedList ? (
<>
@@ -100,10 +100,6 @@ function StorybookInner() {
<ButtonText>Open Shared Prefs Tester</ButtonText>
</Button>
<Admonitions />
<Settings />
<ThemeProvider theme="light">
<Theming />
</ThemeProvider>
@@ -126,6 +122,8 @@ function StorybookInner() {
<Menus />
<Breakpoints />
<Dialogs />
<Admonitions />
<Settings />
<Button
variant="solid"
@@ -150,6 +148,6 @@ function StorybookInner() {
</>
)}
</View>
</CenteredView>
</>
)
}