diff --git a/package.json b/package.json index 9df20604..eec87ce6 100644 --- a/package.json +++ b/package.json @@ -185,6 +185,7 @@ "react-native-web-webview": "^1.0.2", "react-native-webview": "13.6.4", "react-responsive": "^9.0.2", + "react-textarea-autosize": "^8.5.3", "rn-fetch-blob": "^0.12.0", "sentry-expo": "~7.0.1", "statsig-react-native-expo": "^4.6.1", diff --git a/src/screens/Messages/Conversation/MessageInput.tsx b/src/screens/Messages/Conversation/MessageInput.tsx index 1a63b1b5..f9277362 100644 --- a/src/screens/Messages/Conversation/MessageInput.tsx +++ b/src/screens/Messages/Conversation/MessageInput.tsx @@ -1,5 +1,16 @@ import React from 'react' -import {Pressable, TextInput, View} from 'react-native' +import { + Dimensions, + Keyboard, + NativeSyntheticEvent, + Pressable, + TextInput, + TextInputContentSizeChangeEventData, + View, +} from 'react-native' +import {useSafeAreaInsets} from 'react-native-safe-area-context' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {atoms as a, useTheme} from '#/alf' import {Text} from '#/components/Typography' @@ -13,41 +24,67 @@ export function MessageInput({ onFocus: () => void onBlur: () => void }) { + const {_} = useLingui() const t = useTheme() const [message, setMessage] = React.useState('') + const [maxHeight, setMaxHeight] = React.useState() + const [isInputScrollable, setIsInputScrollable] = React.useState(false) + + const {top: topInset} = useSafeAreaInsets() const inputRef = React.useRef(null) const onSubmit = React.useCallback(() => { - onSendMessage(message) + if (message.trim() === '') { + return + } + onSendMessage(message.trimEnd()) setMessage('') setTimeout(() => { inputRef.current?.focus() }, 100) }, [message, onSendMessage]) + const onInputLayout = React.useCallback( + (e: NativeSyntheticEvent) => { + const keyboardHeight = Keyboard.metrics()?.height ?? 0 + const windowHeight = Dimensions.get('window').height + + const max = windowHeight - keyboardHeight - topInset - 100 + const availableSpace = max - e.nativeEvent.contentSize.height + + setMaxHeight(max) + setIsInputScrollable(availableSpace < 30) + }, + [topInset], + ) + return ( void + onFocus: () => void + onBlur: () => void +}) { + const {_} = useLingui() + const t = useTheme() + const [message, setMessage] = React.useState('') + + const onSubmit = React.useCallback(() => { + if (message.trim() === '') { + return + } + onSendMessage(message.trimEnd()) + setMessage('') + }, [message, onSendMessage]) + + const onKeyDown = React.useCallback( + (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + if (e.shiftKey) return + e.preventDefault() + onSubmit() + } + }, + [onSubmit], + ) + + const onChange = React.useCallback( + (e: React.ChangeEvent) => { + setMessage(e.target.value) + }, + [], + ) + + return ( + + + + 🐴 + + + ) +} diff --git a/yarn.lock b/yarn.lock index 8c014e28..749c374f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18981,6 +18981,15 @@ react-test-renderer@18.2.0: react-shallow-renderer "^16.15.0" scheduler "^0.23.0" +react-textarea-autosize@^8.5.3: + version "8.5.3" + resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-8.5.3.tgz#d1e9fe760178413891484847d3378706052dd409" + integrity sha512-XT1024o2pqCuZSuBt9FwHlaDeNtVrtCXu0Rnz88t1jUGheCLa3PhjE1GH8Ctm2axEtvdCl5SUHYschyQ0L5QHQ== + dependencies: + "@babel/runtime" "^7.20.13" + use-composed-ref "^1.3.0" + use-latest "^1.2.1" + react@18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" @@ -21353,6 +21362,16 @@ use-callback-ref@^1.3.0: dependencies: tslib "^2.0.0" +use-composed-ref@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/use-composed-ref/-/use-composed-ref-1.3.0.tgz#3d8104db34b7b264030a9d916c5e94fbe280dbda" + integrity sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ== + +use-isomorphic-layout-effect@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz#497cefb13d863d687b08477d9e5a164ad8c1a6fb" + integrity sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA== + use-latest-callback@^0.1.5: version "0.1.6" resolved "https://registry.yarnpkg.com/use-latest-callback/-/use-latest-callback-0.1.6.tgz#3fa6e7babbb5f9bfa24b5094b22939e1e92ebcf6" @@ -21363,6 +21382,13 @@ use-latest-callback@^0.1.9: resolved "https://registry.yarnpkg.com/use-latest-callback/-/use-latest-callback-0.1.9.tgz#10191dc54257e65a8e52322127643a8940271e2a" integrity sha512-CL/29uS74AwreI/f2oz2hLTW7ZqVeV5+gxFeGudzQrgkCytrHw33G4KbnQOrRlAEzzAFXi7dDLMC9zhWcVpzmw== +use-latest@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/use-latest/-/use-latest-1.2.1.tgz#d13dfb4b08c28e3e33991546a2cee53e14038cf2" + integrity sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw== + dependencies: + use-isomorphic-layout-effect "^1.1.1" + use-sidecar@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/use-sidecar/-/use-sidecar-1.1.2.tgz#2f43126ba2d7d7e117aa5855e5d8f0276dfe73c2"