diff --git a/src/CONST.ts b/src/CONST.ts index c29a7c51e6ef..4024158d0805 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -2879,9 +2879,6 @@ const CONST = { */ ADDITIONAL_ALLOWED_CHARACTERS: 20, - /** types that will show a virtual keyboard in a mobile browser */ - INPUT_TYPES_WITH_KEYBOARD: ['text', 'search', 'tel', 'url', 'email', 'password'], - REFERRAL_PROGRAM: { CONTENT_TYPES: { MONEY_REQUEST: 'request', diff --git a/src/components/SwipeableView/index.native.tsx b/src/components/SwipeableView/index.native.tsx index 07f1d785d0a6..91d851101d4e 100644 --- a/src/components/SwipeableView/index.native.tsx +++ b/src/components/SwipeableView/index.native.tsx @@ -3,40 +3,30 @@ import {PanResponder, View} from 'react-native'; import CONST from '@src/CONST'; import SwipeableViewProps from './types'; -function SwipeableView({children, onSwipeDown, onSwipeUp}: SwipeableViewProps) { +function SwipeableView({children, onSwipeDown}: SwipeableViewProps) { const minimumPixelDistance = CONST.COMPOSER_MAX_HEIGHT; const oldYRef = useRef(0); - const directionRef = useRef<'UP' | 'DOWN' | null>(null); - const panResponder = useRef( PanResponder.create({ - onMoveShouldSetPanResponderCapture: (event, gestureState) => { + // The PanResponder gets focus only when the y-axis movement is over minimumPixelDistance & swipe direction is downwards + // eslint-disable-next-line @typescript-eslint/naming-convention + onMoveShouldSetPanResponderCapture: (_event, gestureState) => { if (gestureState.dy - oldYRef.current > 0 && gestureState.dy > minimumPixelDistance) { - directionRef.current = 'DOWN'; - return true; - } - - if (gestureState.dy - oldYRef.current < 0 && Math.abs(gestureState.dy) > minimumPixelDistance) { - directionRef.current = 'UP'; return true; } oldYRef.current = gestureState.dy; return false; }, - onPanResponderRelease: () => { - if (directionRef.current === 'DOWN' && onSwipeDown) { - onSwipeDown(); - } else if (directionRef.current === 'UP' && onSwipeUp) { - onSwipeUp(); - } - directionRef.current = null; // Reset the direction after the gesture completes - }, + // Calls the callback when the swipe down is released; after the completion of the gesture + onPanResponderRelease: onSwipeDown, }), ).current; - // eslint-disable-next-line react/jsx-props-no-spreading - return {children}; + return ( + // eslint-disable-next-line react/jsx-props-no-spreading + {children} + ); } SwipeableView.displayName = 'SwipeableView'; diff --git a/src/components/SwipeableView/index.tsx b/src/components/SwipeableView/index.tsx index 478935173841..335c3e7dcf03 100644 --- a/src/components/SwipeableView/index.tsx +++ b/src/components/SwipeableView/index.tsx @@ -1,77 +1,4 @@ -import React, {useEffect, useRef} from 'react'; -import {View} from 'react-native'; -import DomUtils from '@libs/DomUtils'; import SwipeableViewProps from './types'; -// Min delta y in px to trigger swipe -const MIN_DELTA_Y = 25; - -function SwipeableView({onSwipeUp, onSwipeDown, style, children}: SwipeableViewProps) { - const ref = useRef(null); - const scrollableChildRef = useRef(null); - const startY = useRef(0); - const isScrolling = useRef(false); - - useEffect(() => { - if (!ref.current) { - return; - } - - const element = ref.current as unknown as HTMLElement; - - const handleTouchStart = (event: TouchEvent) => { - startY.current = event.touches[0].clientY; - }; - - const handleTouchEnd = (event: TouchEvent) => { - const deltaY = event.changedTouches[0].clientY - startY.current; - const isSelecting = DomUtils.isActiveTextSelection(); - let canSwipeDown = true; - let canSwipeUp = true; - if (scrollableChildRef.current) { - canSwipeUp = scrollableChildRef.current.scrollHeight - scrollableChildRef.current.scrollTop === scrollableChildRef.current.clientHeight; - canSwipeDown = scrollableChildRef.current.scrollTop === 0; - } - - if (deltaY > MIN_DELTA_Y && onSwipeDown && !isSelecting && canSwipeDown && !isScrolling.current) { - onSwipeDown(); - } - - if (deltaY < -MIN_DELTA_Y && onSwipeUp && !isSelecting && canSwipeUp && !isScrolling.current) { - onSwipeUp(); - } - isScrolling.current = false; - }; - - const handleScroll = (event: Event) => { - isScrolling.current = true; - if (!event.target || scrollableChildRef.current) { - return; - } - scrollableChildRef.current = event.target as HTMLElement; - }; - - element.addEventListener('touchstart', handleTouchStart); - element.addEventListener('touchend', handleTouchEnd); - element.addEventListener('scroll', handleScroll, true); - - return () => { - element.removeEventListener('touchstart', handleTouchStart); - element.removeEventListener('touchend', handleTouchEnd); - element.removeEventListener('scroll', handleScroll); - }; - }, [onSwipeDown, onSwipeUp]); - - return ( - - {children} - - ); -} - -SwipeableView.displayName = 'SwipeableView'; - -export default SwipeableView; +// Swipeable View is available just on Android/iOS for now. +export default ({children}: SwipeableViewProps) => children; diff --git a/src/components/SwipeableView/types.ts b/src/components/SwipeableView/types.ts index 1f2fbcdc752c..560df7ef5a45 100644 --- a/src/components/SwipeableView/types.ts +++ b/src/components/SwipeableView/types.ts @@ -1,18 +1,11 @@ import {ReactNode} from 'react'; -import {StyleProp, ViewStyle} from 'react-native'; type SwipeableViewProps = { /** The content to be rendered within the SwipeableView */ children: ReactNode; /** Callback to fire when the user swipes down on the child content */ - onSwipeDown?: () => void; - - /** Callback to fire when the user swipes up on the child content */ - onSwipeUp?: () => void; - - /** Style for the wrapper View, applied only for the web version. Not used by the native version, as it brakes the layout. */ - style?: StyleProp; + onSwipeDown: () => void; }; export default SwipeableViewProps; diff --git a/src/hooks/useBlockViewportScroll/index.native.ts b/src/hooks/useBlockViewportScroll/index.native.ts deleted file mode 100644 index 59ee34b1c9f6..000000000000 --- a/src/hooks/useBlockViewportScroll/index.native.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * A hook that blocks viewport scroll when the keyboard is visible. - * It does this by capturing the current scrollY position when the keyboard is shown, then scrolls back to this position smoothly on 'touchend' event. - * This scroll blocking is removed when the keyboard hides. - * This hook is doing nothing on native platforms. - * - * @example - * useBlockViewportScroll(); - */ -function useBlockViewportScroll() { - // This hook is doing nothing on native platforms. - // Check index.ts for web implementation. -} - -export default useBlockViewportScroll; diff --git a/src/hooks/useBlockViewportScroll/index.ts b/src/hooks/useBlockViewportScroll/index.ts deleted file mode 100644 index 5766d59f2bdd..000000000000 --- a/src/hooks/useBlockViewportScroll/index.ts +++ /dev/null @@ -1,43 +0,0 @@ -import {useEffect, useRef} from 'react'; -import Keyboard from '@libs/NativeWebKeyboard'; - -/** - * A hook that blocks viewport scroll when the keyboard is visible. - * It does this by capturing the current scrollY position when the keyboard is shown, then scrolls back to this position smoothly on 'touchend' event. - * This scroll blocking is removed when the keyboard hides. - * This hook is doing nothing on native platforms. - * - * @example - * useBlockViewportScroll(); - */ -function useBlockViewportScroll() { - const optimalScrollY = useRef(0); - const keyboardShowListenerRef = useRef(() => {}); - const keyboardHideListenerRef = useRef(() => {}); - - useEffect(() => { - const handleTouchEnd = () => { - window.scrollTo({top: optimalScrollY.current, behavior: 'smooth'}); - }; - - const handleKeybShow = () => { - optimalScrollY.current = window.scrollY; - window.addEventListener('touchend', handleTouchEnd); - }; - - const handleKeybHide = () => { - window.removeEventListener('touchend', handleTouchEnd); - }; - - keyboardShowListenerRef.current = Keyboard.addListener('keyboardDidShow', handleKeybShow); - keyboardHideListenerRef.current = Keyboard.addListener('keyboardDidHide', handleKeybHide); - - return () => { - keyboardShowListenerRef.current(); - keyboardHideListenerRef.current(); - window.removeEventListener('touchend', handleTouchEnd); - }; - }, []); -} - -export default useBlockViewportScroll; diff --git a/src/libs/DomUtils/index.native.ts b/src/libs/DomUtils/index.native.ts index 8af83968e8d1..0864f1a16ac0 100644 --- a/src/libs/DomUtils/index.native.ts +++ b/src/libs/DomUtils/index.native.ts @@ -2,21 +2,6 @@ import GetActiveElement from './types'; const getActiveElement: GetActiveElement = () => null; -/** - * Checks if there is a text selection within the currently focused input or textarea element. - * - * This function determines whether the currently focused element is an input or textarea, - * and if so, it checks whether there is a text selection (i.e., whether the start and end - * of the selection are at different positions). It assumes that only inputs and textareas - * can have text selections. - * Works only on web. Throws an error on native. - * - * @returns True if there is a text selection within the focused element, false otherwise. - */ -const isActiveTextSelection = () => { - throw new Error('Not implemented in React Native. Use only for web.'); -}; - const requestAnimationFrame = (callback: () => void) => { if (!callback) { return; @@ -27,6 +12,5 @@ const requestAnimationFrame = (callback: () => void) => { export default { getActiveElement, - isActiveTextSelection, requestAnimationFrame, }; diff --git a/src/libs/DomUtils/index.ts b/src/libs/DomUtils/index.ts index 78c2cb37ccc8..6a2eed57fbe6 100644 --- a/src/libs/DomUtils/index.ts +++ b/src/libs/DomUtils/index.ts @@ -2,30 +2,7 @@ import GetActiveElement from './types'; const getActiveElement: GetActiveElement = () => document.activeElement; -/** - * Checks if there is a text selection within the currently focused input or textarea element. - * - * This function determines whether the currently focused element is an input or textarea, - * and if so, it checks whether there is a text selection (i.e., whether the start and end - * of the selection are at different positions). It assumes that only inputs and textareas - * can have text selections. - * Works only on web. Throws an error on native. - * - * @returns True if there is a text selection within the focused element, false otherwise. - */ -const isActiveTextSelection = (): boolean => { - const focused = document.activeElement as HTMLInputElement | HTMLTextAreaElement | null; - if (!focused) { - return false; - } - if (typeof focused.selectionStart === 'number' && typeof focused.selectionEnd === 'number') { - return focused.selectionStart !== focused.selectionEnd; - } - return false; -}; - export default { getActiveElement, - isActiveTextSelection, requestAnimationFrame: window.requestAnimationFrame.bind(window), }; diff --git a/src/libs/NativeWebKeyboard/index.native.ts b/src/libs/NativeWebKeyboard/index.native.ts deleted file mode 100644 index 404bd58075d4..000000000000 --- a/src/libs/NativeWebKeyboard/index.native.ts +++ /dev/null @@ -1,3 +0,0 @@ -import {Keyboard} from 'react-native'; - -export default Keyboard; diff --git a/src/libs/NativeWebKeyboard/index.ts b/src/libs/NativeWebKeyboard/index.ts deleted file mode 100644 index 45223d4d5b42..000000000000 --- a/src/libs/NativeWebKeyboard/index.ts +++ /dev/null @@ -1,136 +0,0 @@ -import {Keyboard} from 'react-native'; -import CONST from '@src/CONST'; - -type InputType = (typeof CONST.INPUT_TYPES_WITH_KEYBOARD)[number]; -type TCallbackFn = () => void; - -const isInputKeyboardType = (element: Element | null): boolean => { - if (element && ((element.tagName === 'INPUT' && CONST.INPUT_TYPES_WITH_KEYBOARD.includes((element as HTMLInputElement).type as InputType)) || element.tagName === 'TEXTAREA')) { - return true; - } - return false; -}; - -const isVisible = (): boolean => { - const focused = document.activeElement; - return isInputKeyboardType(focused); -}; - -const nullFn: () => null = () => null; - -let isKeyboardListenerRunning = false; -let currentVisibleElement: Element | null = null; -const showListeners: TCallbackFn[] = []; -const hideListeners: TCallbackFn[] = []; -const visualViewport = window.visualViewport ?? { - height: window.innerHeight, - width: window.innerWidth, - addEventListener: window.addEventListener.bind(window), - removeEventListener: window.removeEventListener.bind(window), -}; -let previousVPHeight = visualViewport.height; - -const handleViewportResize = (): void => { - if (visualViewport.height < previousVPHeight) { - if (isInputKeyboardType(document.activeElement) && document.activeElement !== currentVisibleElement) { - showListeners.forEach((fn) => fn()); - } - } - - if (visualViewport.height > previousVPHeight) { - if (!isVisible()) { - hideListeners.forEach((fn) => fn()); - } - } - - previousVPHeight = visualViewport.height; - currentVisibleElement = document.activeElement; -}; - -const startKeboardListeningService = (): void => { - isKeyboardListenerRunning = true; - visualViewport.addEventListener('resize', handleViewportResize); -}; - -const addListener = (eventName: 'keyboardDidShow' | 'keyboardDidHide', callbackFn: TCallbackFn): (() => void) => { - if ((eventName !== 'keyboardDidShow' && eventName !== 'keyboardDidHide') || !callbackFn) { - throw new Error('Invalid eventName passed to addListener()'); - } - - if (eventName === 'keyboardDidShow') { - showListeners.push(callbackFn); - } - - if (eventName === 'keyboardDidHide') { - hideListeners.push(callbackFn); - } - - if (!isKeyboardListenerRunning) { - startKeboardListeningService(); - } - - return () => { - if (eventName === 'keyboardDidShow') { - showListeners.filter((fn) => fn !== callbackFn); - } - - if (eventName === 'keyboardDidHide') { - hideListeners.filter((fn) => fn !== callbackFn); - } - - if (isKeyboardListenerRunning && !showListeners.length && !hideListeners.length) { - visualViewport.removeEventListener('resize', handleViewportResize); - isKeyboardListenerRunning = false; - } - }; -}; - -export default { - /** - * Whether the keyboard is last known to be visible. - */ - isVisible, - /** - * Dismisses the active keyboard and removes focus. - */ - dismiss: Keyboard.dismiss, - /** - * The `addListener` function connects a JavaScript function to an identified native - * keyboard notification event. - * - * This function then returns the reference to the listener. - * - * {string} eventName The `nativeEvent` is the string that identifies the event you're listening for. This - * can be any of the following: - * - * - `keyboardWillShow` - * - `keyboardDidShow` - * - `keyboardWillHide` - * - `keyboardDidHide` - * - `keyboardWillChangeFrame` - * - `keyboardDidChangeFrame` - * - * Note that if you set `android:windowSoftInputMode` to `adjustResize` or `adjustNothing`, - * only `keyboardDidShow` and `keyboardDidHide` events will be available on Android. - * `keyboardWillShow` as well as `keyboardWillHide` are generally not available on Android - * since there is no native corresponding event. - * - * On Web only two events are available: - * - * - `keyboardDidShow` - * - `keyboardDidHide` - * - * {function} callback function to be called when the event fires. - */ - addListener, - /** - * Useful for syncing TextInput (or other keyboard accessory view) size of - * position changes with keyboard movements. - * Not working on web. - */ - scheduleLayoutAnimation: nullFn, - /** - * Return the metrics of the soft-keyboard if visible. Currently not working on web. - */ - metrics: nullFn, -}; diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index 09d147b05f69..649bcee6da18 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -15,7 +15,6 @@ import ScreenWrapper from '@components/ScreenWrapper'; import TaskHeaderActionButton from '@components/TaskHeaderActionButton'; import withCurrentReportID, {withCurrentReportIDDefaultProps, withCurrentReportIDPropTypes} from '@components/withCurrentReportID'; import withViewportOffsetTop from '@components/withViewportOffsetTop'; -import useBlockViewportScroll from '@hooks/useBlockViewportScroll'; import useLocalize from '@hooks/useLocalize'; import usePrevious from '@hooks/usePrevious'; import useWindowDimensions from '@hooks/useWindowDimensions'; @@ -151,7 +150,6 @@ function ReportScreen({ const styles = useThemeStyles(); const {translate} = useLocalize(); const {isSmallScreenWidth} = useWindowDimensions(); - useBlockViewportScroll(); const firstRenderRef = useRef(true); const flatListRef = useRef(); diff --git a/src/pages/home/report/ReportFooter.js b/src/pages/home/report/ReportFooter.js index 9f973674d6a7..e5dd5da19ad5 100644 --- a/src/pages/home/report/ReportFooter.js +++ b/src/pages/home/report/ReportFooter.js @@ -92,10 +92,7 @@ function ReportFooter(props) { )} {!hideComposer && (props.shouldShowComposeInput || !props.isSmallScreenWidth) && ( - +