Use RNKC for composer keyboard avoiding view (#10997)

Co-authored-by: Eric Bailey <git@esb.lol>
This commit is contained in:
Samuel Newman
2026-07-01 00:15:50 +03:00
committed by GitHub
parent 5e7343b0d2
commit 0adf500edf
2 changed files with 34 additions and 46 deletions
-9
View File
@@ -1940,15 +1940,6 @@
} }
}, },
"src/view/com/composer/Composer.tsx": { "src/view/com/composer/Composer.tsx": {
"@typescript-eslint/no-explicit-any": {
"count": 2
},
"@typescript-eslint/no-misused-promises": {
"count": 4
},
"@typescript-eslint/no-unsafe-member-access": {
"count": 2
},
"react-hooks/immutability": { "react-hooks/immutability": {
"count": 2 "count": 2
}, },
+34 -37
View File
@@ -13,7 +13,6 @@ import {
ActivityIndicator, ActivityIndicator,
BackHandler, BackHandler,
Keyboard, Keyboard,
KeyboardAvoidingView,
type LayoutChangeEvent, type LayoutChangeEvent,
ScrollView, ScrollView,
type StyleProp, type StyleProp,
@@ -21,6 +20,7 @@ import {
View, View,
type ViewStyle, type ViewStyle,
} from 'react-native' } from 'react-native'
import {KeyboardAvoidingView} from 'react-native-keyboard-controller'
// @ts-expect-error no type definition // @ts-expect-error no type definition
import ProgressCircle from 'react-native-progress/Circle' import ProgressCircle from 'react-native-progress/Circle'
import Animated, { import Animated, {
@@ -72,7 +72,6 @@ import {
type SupportedMimeTypes, type SupportedMimeTypes,
VIDEO_MAX_DURATION_MS, VIDEO_MAX_DURATION_MS,
} from '#/lib/constants' } from '#/lib/constants'
import {useIsKeyboardVisible} from '#/lib/hooks/useIsKeyboardVisible'
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
import {createVideoTelemetry} from '#/lib/media/video/telemetry' import {createVideoTelemetry} from '#/lib/media/video/telemetry'
import {mimeToExt} from '#/lib/media/video/util' import {mimeToExt} from '#/lib/media/video/util'
@@ -287,7 +286,6 @@ export const ComposePost = ({
const {data: preferences} = usePreferencesQuery() const {data: preferences} = usePreferencesQuery()
const navigation = useNavigation<NavigationProp>() const navigation = useNavigation<NavigationProp>()
const [isKeyboardVisible] = useIsKeyboardVisible({iosUseWillEvents: true})
const [isPublishing, setIsPublishing] = useState(false) const [isPublishing, setIsPublishing] = useState(false)
const [publishingStage, setPublishingStage] = useState('') const [publishingStage, setPublishingStage] = useState('')
const [error, setError] = useState('') const [error, setError] = useState('')
@@ -856,17 +854,9 @@ export const ComposePost = ({
const viewStyles = useMemo( const viewStyles = useMemo(
() => ({ () => ({
paddingTop: IS_ANDROID ? insets.top : 0, paddingTop: IS_ANDROID ? insets.top : 0,
paddingBottom: paddingBottom: insets.bottom,
// iOS - when keyboard is closed, keep the bottom bar in the safe area
(IS_IOS && !isKeyboardVisible) ||
// Android - Android >=35 KeyboardAvoidingView adds double padding when
// keyboard is closed, so we subtract that in the offset and add it back
// here when the keyboard is open
(IS_ANDROID && isKeyboardVisible)
? insets.bottom
: 0,
}), }),
[insets, isKeyboardVisible], [insets.top, insets.bottom],
) )
const onPressCancel = useCallback(() => { const onPressCancel = useCallback(() => {
@@ -1100,13 +1090,13 @@ export const ComposePost = ({
posts, posts,
} }
} }
} catch (waitErr: any) { } catch (waitErr) {
logger.info(`composer: waiting for app view failed`, { logger.info(`composer: waiting for app view failed`, {
safeMessage: waitErr, safeMessage: waitErr,
}) })
} }
} catch (e: any) { } catch (e) {
logger.error(e, { logger.error(e instanceof Error ? e : String(e), {
message: `Composer: create post failed`, message: `Composer: create post failed`,
hasImages: filteredThread.posts.some( hasImages: filteredThread.posts.some(
p => p =>
@@ -1115,7 +1105,7 @@ export const ComposePost = ({
), ),
}) })
let err = cleanError(e.message) let err = e instanceof Error ? cleanError(e.message) : String(e)
if ( if (
e instanceof apilib.ReplyDeletedError || e instanceof apilib.ReplyDeletedError ||
err.includes('not locate record') err.includes('not locate record')
@@ -1417,8 +1407,8 @@ export const ComposePost = ({
publishingStage={publishingStage} publishingStage={publishingStage}
topBarAnimatedStyle={topBarAnimatedStyle} topBarAnimatedStyle={topBarAnimatedStyle}
onCancel={onPressCancel} onCancel={onPressCancel}
onPublish={onPressPublish} onPublish={() => void onPressPublish()}
onSelectDraft={handleSelectDraft} onSelectDraft={draft => void handleSelectDraft(draft)}
onSaveDraft={saveCurrentDraft} onSaveDraft={saveCurrentDraft}
onDiscard={handleClearComposer} onDiscard={handleClearComposer}
isEmpty={isComposerEmpty} isEmpty={isComposerEmpty}
@@ -1531,7 +1521,7 @@ export const ComposePost = ({
{allPostsWithinLimit && ( {allPostsWithinLimit && (
<Prompt.Action <Prompt.Action
cta={composerState.draftId ? l`Save changes` : l`Save draft`} cta={composerState.draftId ? l`Save changes` : l`Save draft`}
onPress={handleSaveDraft} onPress={() => void handleSaveDraft()}
color="primary" color="primary"
/> />
)} )}
@@ -1694,7 +1684,7 @@ let ComposerPost = memo(function ComposerPost({
postId: post.id, postId: post.id,
}) })
}} }}
onPhotoPasted={onPhotoPasted} onPhotoPasted={uri => void onPhotoPasted(uri)}
onNewLink={onNewLink} onNewLink={onNewLink}
onError={onError} onError={onError}
onPressPublish={onPublish} onPressPublish={onPublish}
@@ -2188,7 +2178,7 @@ function ComposerFooter({
}), }),
).catch(e => { ).catch(e => {
logger.error(`createComposerImage failed`, { logger.error(`createComposerImage failed`, {
safeMessage: e.message, safeMessage: e instanceof Error ? e.message : String(e),
}) })
}) })
@@ -2425,24 +2415,31 @@ function useScrollTracker({
} }
function useKeyboardVerticalOffset() { function useKeyboardVerticalOffset() {
const {top, bottom} = useSafeAreaInsets() const insets = useSafeAreaInsets()
// Android etc // the keyboardavoidingview has bottom padding to avoid being obscured by the safe area when keyboard is closed.
if (!IS_IOS) { // however, this leads to a gap when the keyboard is open. we account for that by subtracting the bottom inset when open.
// need to account for the edge-to-edge nav bar let keyboardVerticalOffset = insets.bottom * -1
return bottom * -1
// iOS requires a bit of extra offset to account for the native sheet not being at the top of the screen
if (IS_IOS) {
// they ditched the gap behaviour on 26
if (IS_LIQUID_GLASS) {
keyboardVerticalOffset += insets.top
}
// iPhone SE
else if (insets.top === 20) {
keyboardVerticalOffset += 40
}
// all other iPhones on <26
else {
keyboardVerticalOffset += insets.top + 10
}
} }
// they ditched the gap behaviour on 26 return keyboardVerticalOffset
if (IS_LIQUID_GLASS) {
return top
}
// iPhone SE
if (top === 20) return 40
// all other iPhones on <26
return top + 10
} }
async function whenAppViewReady( async function whenAppViewReady(