Skip to content

Commit b328461

Browse files
authored
Revert "Improve native top spacing for bottom docked modals when keyboard shows"
1 parent b65328f commit b328461

5 files changed

Lines changed: 15 additions & 118 deletions

File tree

src/components/FeatureTrainingModal.tsx

Lines changed: 6 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
import type {VideoReadyForDisplayEvent} from 'expo-av';
22
import type {ImageContentFit} from 'expo-image';
3-
import React, {useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState} from 'react';
3+
import React, {useCallback, useEffect, useLayoutEffect, useRef, useState} from 'react';
44
import {Image, InteractionManager, View} from 'react-native';
5-
// eslint-disable-next-line no-restricted-imports
6-
import type {ImageResizeMode, ImageSourcePropType, LayoutChangeEvent, ScrollView as RNScrollView, StyleProp, TextStyle, ViewStyle} from 'react-native';
5+
import type {ImageResizeMode, ImageSourcePropType, StyleProp, TextStyle, ViewStyle} from 'react-native';
76
import {GestureHandlerRootView} from 'react-native-gesture-handler';
87
import type {MergeExclusive} from 'type-fest';
98
import useLocalize from '@hooks/useLocalize';
109
import useNetwork from '@hooks/useNetwork';
1110
import useResponsiveLayout from '@hooks/useResponsiveLayout';
12-
import useSafeAreaInsets from '@hooks/useSafeAreaInsets';
1311
import useStyleUtils from '@hooks/useStyleUtils';
1412
import useThemeStyles from '@hooks/useThemeStyles';
1513
import {parseFSAttributes} from '@libs/Fullstory';
@@ -211,10 +209,6 @@ function FeatureTrainingModal({
211209
const {shouldUseNarrowLayout} = useResponsiveLayout();
212210
const {isOffline} = useNetwork();
213211
const hasHelpButtonBeenPressed = useRef(false);
214-
const scrollViewRef = useRef<RNScrollView>(null);
215-
const [containerHeight, setContainerHeight] = useState(0);
216-
const [contentHeight, setContentHeight] = useState(0);
217-
const insets = useSafeAreaInsets();
218212

219213
useEffect(() => {
220214
InteractionManager.runAfterInteractions(() => {
@@ -361,18 +355,8 @@ function FeatureTrainingModal({
361355
*/
362356
useLayoutEffect(parseFSAttributes, []);
363357

364-
// Scrolls modal to the bottom when keyboard appears so the action buttons are visible.
365-
useEffect(() => {
366-
if (contentHeight <= containerHeight || onboardingIsMediumOrLargerScreenWidth || !shouldUseScrollView) {
367-
return;
368-
}
369-
scrollViewRef.current?.scrollToEnd({animated: false});
370-
}, [contentHeight, containerHeight, onboardingIsMediumOrLargerScreenWidth, shouldUseScrollView]);
371-
372358
const Wrapper = shouldUseScrollView ? ScrollView : View;
373359

374-
const wrapperStyles = useMemo(() => (shouldUseScrollView ? StyleUtils.getScrollableFeatureTrainingModalStyles(insets) : {}), [shouldUseScrollView, StyleUtils, insets]);
375-
376360
return (
377361
<Modal
378362
avoidKeyboard={avoidKeyboard}
@@ -402,13 +386,9 @@ function FeatureTrainingModal({
402386
shouldUseReanimatedModal
403387
>
404388
<Wrapper
405-
scrollsToTop={false}
406-
style={[styles.mh100, onboardingIsMediumOrLargerScreenWidth && StyleUtils.getWidthStyle(width), wrapperStyles.style]}
407-
contentContainerStyle={wrapperStyles.containerStyle}
389+
style={[styles.mh100, onboardingIsMediumOrLargerScreenWidth && StyleUtils.getWidthStyle(width)]}
390+
contentContainerStyle={shouldUseScrollView ? styles.pb5 : undefined}
408391
keyboardShouldPersistTaps={shouldUseScrollView ? 'handled' : undefined}
409-
ref={shouldUseScrollView ? scrollViewRef : undefined}
410-
onLayout={shouldUseScrollView ? (e: LayoutChangeEvent) => setContainerHeight(e.nativeEvent.layout.height) : undefined}
411-
onContentSizeChange={shouldUseScrollView ? (_w: number, h: number) => setContentHeight(h) : undefined}
412392
fsClass={CONST.FULL_STORY.UNMASK}
413393
testID={CONST.FULL_STORY.UNMASK}
414394
>
@@ -417,20 +397,9 @@ function FeatureTrainingModal({
417397
</View>
418398
<View style={[styles.mt5, styles.mh5, contentOuterContainerStyles]}>
419399
{!!title && !!description && (
420-
<View
421-
style={[
422-
onboardingIsMediumOrLargerScreenWidth ? [styles.gap1, styles.mb8] : [shouldRenderHTMLDescription ? styles.mb5 : styles.mb10],
423-
contentInnerContainerStyles,
424-
]}
425-
>
400+
<View style={[onboardingIsMediumOrLargerScreenWidth ? [styles.gap1, styles.mb8] : [styles.mb10], contentInnerContainerStyles]}>
426401
{typeof title === 'string' ? <Text style={[styles.textHeadlineH1, titleStyles]}>{title}</Text> : title}
427-
{shouldRenderHTMLDescription ? (
428-
<View style={styles.mb2}>
429-
<RenderHTML html={description} />
430-
</View>
431-
) : (
432-
<Text style={styles.textSupporting}>{description}</Text>
433-
)}
402+
{shouldRenderHTMLDescription ? <RenderHTML html={description} /> : <Text style={styles.textSupporting}>{description}</Text>}
434403
{secondaryDescription.length > 0 && <Text style={[styles.textSupporting, styles.mt4]}>{secondaryDescription}</Text>}
435404
{children}
436405
</View>

src/components/Modal/BaseModal.tsx

Lines changed: 2 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import React, {forwardRef, useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react';
1+
import React, {forwardRef, useCallback, useContext, useEffect, useMemo, useRef} from 'react';
22
import {View} from 'react-native';
3-
import type {LayoutChangeEvent} from 'react-native';
43
import type {ModalProps as ReactNativeModalProps} from 'react-native-modal';
54
import ReactNativeModal from 'react-native-modal';
65
import type {ValueOf} from 'type-fest';
@@ -18,7 +17,6 @@ import useTheme from '@hooks/useTheme';
1817
import useThemeStyles from '@hooks/useThemeStyles';
1918
import useWindowDimensions from '@hooks/useWindowDimensions';
2019
import ComposerFocusManager from '@libs/ComposerFocusManager';
21-
import getPlatform from '@libs/getPlatform';
2220
import NarrowPaneContext from '@libs/Navigation/AppNavigator/Navigators/NarrowPaneContext';
2321
import Overlay from '@libs/Navigation/AppNavigator/Navigators/Overlay';
2422
import Navigation from '@libs/Navigation/Navigation';
@@ -111,8 +109,6 @@ function BaseModal(
111109
const {sidePanelOffset} = useSidePanel();
112110
const sidePanelStyle = shouldApplySidePanelOffset && !isSmallScreenWidth ? {paddingRight: sidePanelOffset.current} : undefined;
113111
const keyboardStateContextValue = useKeyboardState();
114-
const [modalOverlapsWithTopSafeArea, setModalOverlapsWithTopSafeArea] = useState(false);
115-
const [modalHeight, setModalHeight] = useState(0);
116112

117113
const insets = useSafeAreaInsets();
118114

@@ -207,29 +203,6 @@ function BaseModal(
207203
ComposerFocusManager.setReadyToFocus(uniqueModalId);
208204
};
209205

210-
// Checks if modal overlaps with topSafeArea. Used to offset tall bottom docked modals with keyboard.
211-
useEffect(() => {
212-
if (type !== CONST.MODAL.MODAL_TYPE.BOTTOM_DOCKED || getPlatform() === CONST.PLATFORM.WEB) {
213-
return;
214-
}
215-
const {paddingTop} = StyleUtils.getPlatformSafeAreaPadding(insets);
216-
const availableHeight = windowHeight - modalHeight - keyboardStateContextValue.keyboardActiveHeight - paddingTop;
217-
setModalOverlapsWithTopSafeArea((keyboardStateContextValue.isKeyboardAnimatingRef.current || keyboardStateContextValue.isKeyboardActive) && Math.floor(availableHeight) <= 0);
218-
}, [
219-
StyleUtils,
220-
insets,
221-
keyboardStateContextValue.isKeyboardActive,
222-
keyboardStateContextValue.isKeyboardAnimatingRef,
223-
keyboardStateContextValue.keyboardActiveHeight,
224-
modalHeight,
225-
type,
226-
windowHeight,
227-
]);
228-
229-
const onViewLayout = (e: LayoutChangeEvent) => {
230-
setModalHeight(e.nativeEvent.layout.height);
231-
};
232-
233206
const {
234207
modalStyle,
235208
modalContainerStyle,
@@ -253,9 +226,8 @@ function BaseModal(
253226
innerContainerStyle,
254227
outerStyle,
255228
shouldUseModalPaddingStyle,
256-
modalOverlapsWithTopSafeArea,
257229
),
258-
[StyleUtils, type, windowWidth, windowHeight, isSmallScreenWidth, popoverAnchorPosition, innerContainerStyle, outerStyle, shouldUseModalPaddingStyle, modalOverlapsWithTopSafeArea],
230+
[StyleUtils, type, windowWidth, windowHeight, isSmallScreenWidth, popoverAnchorPosition, innerContainerStyle, outerStyle, shouldUseModalPaddingStyle],
259231
);
260232

261233
const modalPaddingStyles = useMemo(() => {
@@ -378,7 +350,6 @@ function BaseModal(
378350
shouldPreventScroll={shouldPreventScrollOnFocus}
379351
>
380352
<View
381-
onLayout={onViewLayout}
382353
style={[styles.defaultModalContainer, modalContainerStyle, modalPaddingStyles, !isVisible && styles.pointerEventsNone]}
383354
ref={ref}
384355
>

src/components/withKeyboardState.tsx

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type {ReactElement, RefObject} from 'react';
1+
import type {MutableRefObject, ReactElement} from 'react';
22
import React, {createContext, useCallback, useEffect, useMemo, useRef, useState} from 'react';
33
import {KeyboardEvents, useKeyboardHandler} from 'react-native-keyboard-controller';
44
import {runOnJS} from 'react-native-reanimated';
@@ -16,25 +16,20 @@ type KeyboardStateContextValue = {
1616
/** Height of the keyboard in pixels */
1717
keyboardHeight: number;
1818

19-
/** Future or present height of the keyboard in pixels. Available together with isKeyboardActive. */
20-
keyboardActiveHeight: number;
21-
2219
/** Ref to check if the keyboard is animating */
23-
isKeyboardAnimatingRef: RefObject<boolean>;
20+
isKeyboardAnimatingRef: MutableRefObject<boolean>;
2421
};
2522

2623
const KeyboardStateContext = createContext<KeyboardStateContextValue>({
2724
isKeyboardShown: false,
2825
isKeyboardActive: false,
2926
keyboardHeight: 0,
30-
keyboardActiveHeight: 0,
3127
isKeyboardAnimatingRef: {current: false},
3228
});
3329

3430
function KeyboardStateProvider({children}: ChildrenProps): ReactElement | null {
3531
const {bottom} = useSafeAreaInsets();
3632
const [keyboardHeight, setKeyboardHeight] = useState(0);
37-
const [keyboardActiveHeight, setKeyboardActiveHeight] = useState(0);
3833
const isKeyboardAnimatingRef = useRef(false);
3934
const [isKeyboardActive, setIsKeyboardActive] = useState(false);
4035

@@ -47,13 +42,11 @@ function KeyboardStateProvider({children}: ChildrenProps): ReactElement | null {
4742
setKeyboardHeight(0);
4843
setIsKeyboardActive(false);
4944
});
50-
const keyboardWillShowListener = KeyboardEvents.addListener('keyboardWillShow', (e) => {
45+
const keyboardWillShowListener = KeyboardEvents.addListener('keyboardWillShow', () => {
5146
setIsKeyboardActive(true);
52-
setKeyboardActiveHeight(e.height);
5347
});
5448
const keyboardWillHideListener = KeyboardEvents.addListener('keyboardWillHide', () => {
5549
setIsKeyboardActive(false);
56-
setKeyboardActiveHeight(0);
5750
});
5851

5952
return () => {
@@ -87,12 +80,11 @@ function KeyboardStateProvider({children}: ChildrenProps): ReactElement | null {
8780
const contextValue = useMemo(
8881
() => ({
8982
keyboardHeight,
90-
keyboardActiveHeight,
9183
isKeyboardShown: keyboardHeight !== 0,
9284
isKeyboardAnimatingRef,
9385
isKeyboardActive,
9486
}),
95-
[isKeyboardActive, keyboardActiveHeight, keyboardHeight],
87+
[isKeyboardActive, keyboardHeight],
9688
);
9789
return <KeyboardStateContext.Provider value={contextValue}>{children}</KeyboardStateContext.Provider>;
9890
}

src/styles/utils/generators/ModalStyleUtils.ts

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import type {ViewStyle} from 'react-native';
22
import type {ModalProps} from 'react-native-modal';
3-
import {isMobileSafari} from '@libs/Browser';
43
import type {ThemeStyles} from '@styles/index';
54
import variables from '@styles/variables';
65
import CONST from '@src/CONST';
@@ -43,20 +42,11 @@ type GetModalStylesStyleUtil = {
4342
innerContainerStyle?: ViewStyle,
4443
outerStyle?: ViewStyle,
4544
shouldUseModalPaddingStyle?: boolean,
46-
modalOverlapsWithTopSafeArea?: boolean,
4745
) => GetModalStyles;
4846
};
4947

5048
const createModalStyleUtils: StyleUtilGenerator<GetModalStylesStyleUtil> = ({theme, styles}) => ({
51-
getModalStyles: (
52-
type,
53-
windowDimensions,
54-
popoverAnchorPosition = {},
55-
innerContainerStyle = {},
56-
outerStyle = {},
57-
shouldUseModalPaddingStyle = true,
58-
modalOverlapsWithTopSafeArea = false,
59-
): GetModalStyles => {
49+
getModalStyles: (type, windowDimensions, popoverAnchorPosition = {}, innerContainerStyle = {}, outerStyle = {}, shouldUseModalPaddingStyle = true): GetModalStyles => {
6050
const {windowWidth, isSmallScreenWidth} = windowDimensions;
6151

6252
let modalStyle: GetModalStyles['modalStyle'] = {
@@ -228,19 +218,14 @@ const createModalStyleUtils: StyleUtilGenerator<GetModalStylesStyleUtil> = ({the
228218
justifyContent: 'center',
229219
overflow: 'hidden',
230220
boxShadow: theme.shadow,
231-
// Workaround for Safari not supporting interactive-widget=resizes-content, sets max height of a container modal.
232-
// This allows better scrolling experience after keyboard shows for modals with input, that are larger than remaining screen height.
233-
// More info https://github.com/Expensify/App/pull/62799#issuecomment-2943136220.
234-
...(isMobileSafari() ? {maxHeight: `${windowDimensions.windowHeight}px`} : {}),
235221
};
236222

237223
if (shouldUseModalPaddingStyle) {
238224
modalContainerStyle.paddingTop = variables.componentBorderRadiusLarge;
239225
modalContainerStyle.paddingBottom = variables.componentBorderRadiusLarge;
240226
}
241227

242-
shouldAddBottomSafeAreaPadding = innerContainerStyle.paddingBottom !== 0;
243-
shouldAddTopSafeAreaMargin = modalOverlapsWithTopSafeArea;
228+
shouldAddBottomSafeAreaPadding = true;
244229
swipeDirection = undefined;
245230
animationIn = 'slideInUp';
246231
animationOut = 'slideOutDown';

src/styles/utils/index.ts

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type {OnyxEntry} from 'react-native-onyx';
44
import type {EdgeInsets} from 'react-native-safe-area-context';
55
import type {ValueOf} from 'type-fest';
66
import type ImageSVGProps from '@components/ImageSVG/types';
7-
import {isMobile, isMobileChrome} from '@libs/Browser';
7+
import {isMobile} from '@libs/Browser';
88
import getPlatform from '@libs/getPlatform';
99
import {hashText} from '@libs/UserUtils';
1010
// eslint-disable-next-line no-restricted-imports
@@ -1234,7 +1234,6 @@ const staticStyleUtils = {
12341234
getBackgroundAndBorderStyle,
12351235
getBackgroundColorStyle,
12361236
getBackgroundColorWithOpacityStyle,
1237-
getCombinedSpacing,
12381237
getPaddingLeft,
12391238
getPaddingRight,
12401239
getPaddingBottom,
@@ -1833,25 +1832,6 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({
18331832
styleObj[key] = null;
18341833
return styleObj;
18351834
}, {} as Nullable<K>) as K,
1836-
getScrollableFeatureTrainingModalStyles: (
1837-
insets: EdgeInsets,
1838-
): {
1839-
style?: ViewStyle;
1840-
containerStyle?: ViewStyle;
1841-
} => {
1842-
const {paddingBottom: safeAreaPaddingBottom} = getPlatformSafeAreaPadding(insets);
1843-
1844-
const paddingBottom = getCombinedSpacing(styles.pb5.paddingBottom, safeAreaPaddingBottom, true);
1845-
// Forces scroll on modal when keyboard is open and the modal larger than remaining screen height.
1846-
return {
1847-
style: isMobileChrome()
1848-
? {
1849-
maxHeight: '100dvh',
1850-
}
1851-
: {},
1852-
containerStyle: {paddingBottom},
1853-
};
1854-
},
18551835
});
18561836

18571837
type StyleUtilsType = ReturnType<typeof createStyleUtils>;

0 commit comments

Comments
 (0)