diff --git a/android/app/build.gradle b/android/app/build.gradle
index c7974572e665..7bf56a7a1177 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -90,8 +90,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
- versionCode 1001039600
- versionName "1.3.96-0"
+ versionCode 1001039602
+ versionName "1.3.96-2"
}
flavorDimensions "default"
diff --git a/assets/images/bell.svg b/assets/images/bell.svg
index 6ba600dc695b..5a6b411185a9 100644
--- a/assets/images/bell.svg
+++ b/assets/images/bell.svg
@@ -1,6 +1 @@
-
-
-
+
\ No newline at end of file
diff --git a/assets/images/home-background--android.svg b/assets/images/home-background--android.svg
index 507aecf04836..2b72b6ccabe9 100644
--- a/assets/images/home-background--android.svg
+++ b/assets/images/home-background--android.svg
@@ -1,6555 +1 @@
-
-
-
+
\ No newline at end of file
diff --git a/babel.config.js b/babel.config.js
index 7de6926c850d..64a433936eb7 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -17,16 +17,8 @@ const defaultPlugins = [
];
const webpack = {
- env: {
- production: {
- presets: defaultPresets,
- plugins: [...defaultPlugins, 'transform-remove-console'],
- },
- development: {
- presets: defaultPresets,
- plugins: defaultPlugins,
- },
- },
+ presets: defaultPresets,
+ plugins: defaultPlugins,
};
const metro = {
@@ -78,6 +70,11 @@ const metro = {
},
],
],
+ env: {
+ production: {
+ plugins: ['transform-remove-console', {exclude: ['error', 'warn']}],
+ },
+ },
};
/*
@@ -102,11 +99,19 @@ if (process.env.CAPTURE_METRICS === 'true') {
]);
}
-module.exports = ({caller}) => {
+module.exports = (api) => {
+ console.debug('babel.config.js');
+ console.debug(' - api.version:', api.version);
+ console.debug(' - api.env:', api.env());
+ console.debug(' - process.env.NODE_ENV:', process.env.NODE_ENV);
+ console.debug(' - process.env.BABEL_ENV:', process.env.BABEL_ENV);
+
// For `react-native` (iOS/Android) caller will be "metro"
// For `webpack` (Web) caller will be "@babel-loader"
// For jest, it will be babel-jest
// For `storybook` there won't be any config at all so we must give default argument of an empty object
- const runningIn = caller((args = {}) => args.name);
+ const runningIn = api.caller((args = {}) => args.name);
+ console.debug(' - running in: ', runningIn);
+
return ['metro', 'babel-jest'].includes(runningIn) ? metro : webpack;
};
diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist
index 94d2986fd111..e31b05dcece6 100644
--- a/ios/NewExpensify/Info.plist
+++ b/ios/NewExpensify/Info.plist
@@ -40,7 +40,7 @@
CFBundleVersion
- 1.3.96.0
+ 1.3.96.2
ITSAppUsesNonExemptEncryption
LSApplicationQueriesSchemes
diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist
index 9478336965cf..1b3695499183 100644
--- a/ios/NewExpensifyTests/Info.plist
+++ b/ios/NewExpensifyTests/Info.plist
@@ -19,6 +19,6 @@
CFBundleSignature
????
CFBundleVersion
- 1.3.96.0
+ 1.3.96.2
diff --git a/package-lock.json b/package-lock.json
index c263632e7615..b4513f4359ee 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "new.expensify",
- "version": "1.3.96-0",
+ "version": "1.3.96-2",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "new.expensify",
- "version": "1.3.96-0",
+ "version": "1.3.96-2",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
diff --git a/package.json b/package.json
index dcbe95560bc7..add29281e08b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
- "version": "1.3.96-0",
+ "version": "1.3.96-2",
"author": "Expensify, Inc.",
"homepage": "https://new.expensify.com",
"description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",
diff --git a/src/ROUTES.ts b/src/ROUTES.ts
index bcc4685368cb..864e8934ad88 100644
--- a/src/ROUTES.ts
+++ b/src/ROUTES.ts
@@ -2,14 +2,11 @@ import {ValueOf} from 'type-fest';
import CONST from './CONST';
/**
- * This is a file containing constants for all of the routes we want to be able to go to
+ * This is a file containing constants for all the routes we want to be able to go to
*/
/**
- * This is a file containing constants for all of the routes we want to be able to go to
- * Returns the URL with an encoded URI component for the backTo param which can be added to the end of URLs
- * @param backTo
- * @returns
+ * Builds a URL with an encoded URI component for the `backTo` param which can be added to the end of URLs
*/
function getUrlWithBackToParam(url: string, backTo?: string): string {
const backToParam = backTo ? `${url.includes('?') ? '&' : '?'}backTo=${encodeURIComponent(backTo)}` : '';
@@ -111,7 +108,10 @@ export default {
route: 'settings/profile/personal-details/address/country',
getRoute: (country: string, backTo?: string) => getUrlWithBackToParam(`settings/profile/personal-details/address/country?country=${country}`, backTo),
},
- SETTINGS_CONTACT_METHODS: 'settings/profile/contact-methods',
+ SETTINGS_CONTACT_METHODS: {
+ route: 'settings/profile/contact-methods',
+ getRoute: (backTo?: string) => getUrlWithBackToParam('settings/profile/contact-methods', backTo),
+ },
SETTINGS_CONTACT_METHOD_DETAILS: {
route: 'settings/profile/contact-methods/:contactMethod/details',
getRoute: (contactMethod: string) => `settings/profile/contact-methods/${encodeURIComponent(contactMethod)}/details`,
diff --git a/src/components/ConnectBankAccountButton.js b/src/components/ConnectBankAccountButton.js
index 64d2421c7d37..2c66bcc200da 100644
--- a/src/components/ConnectBankAccountButton.js
+++ b/src/components/ConnectBankAccountButton.js
@@ -30,7 +30,7 @@ const defaultProps = {
};
function ConnectBankAccountButton(props) {
- const activeRoute = Navigation.getActiveRoute().replace(/\?.*/, '');
+ const activeRoute = Navigation.getActiveRouteWithoutParams();
return props.network.isOffline ? (
{`${props.translate('common.youAppearToBeOffline')} ${props.translate('common.thisFeatureRequiresInternet')}`}
diff --git a/src/components/CountrySelector.js b/src/components/CountrySelector.js
index 93a90dcf6be9..c2426c5b7b0b 100644
--- a/src/components/CountrySelector.js
+++ b/src/components/CountrySelector.js
@@ -53,7 +53,7 @@ function CountrySelector({errorText, value: countryCode, onInputChange, forwarde
descriptionTextStyle={countryTitleDescStyle}
description={translate('common.country')}
onPress={() => {
- const activeRoute = Navigation.getActiveRoute().replace(/\?.*/, '');
+ const activeRoute = Navigation.getActiveRouteWithoutParams();
Navigation.navigate(ROUTES.SETTINGS_PERSONAL_DETAILS_ADDRESS_COUNTRY.getRoute(countryCode, activeRoute));
}}
/>
diff --git a/src/components/Image/index.js b/src/components/Image/index.js
index c2800511ff45..ef1a69e19c12 100644
--- a/src/components/Image/index.js
+++ b/src/components/Image/index.js
@@ -69,4 +69,5 @@ const ImageWithOnyx = React.memo(
imagePropsAreEqual,
);
ImageWithOnyx.resizeMode = RESIZE_MODES;
+
export default ImageWithOnyx;
diff --git a/src/components/Image/index.native.js b/src/components/Image/index.native.js
index 52ac503081e6..cf5320392d1b 100644
--- a/src/components/Image/index.native.js
+++ b/src/components/Image/index.native.js
@@ -59,4 +59,5 @@ const ImageWithOnyx = withOnyx({
})(Image);
ImageWithOnyx.resizeMode = RESIZE_MODES;
ImageWithOnyx.resolveDimensions = resolveDimensions;
+
export default ImageWithOnyx;
diff --git a/src/components/ImageWithSizeCalculation.js b/src/components/ImageWithSizeCalculation.tsx
similarity index 66%
rename from src/components/ImageWithSizeCalculation.js
rename to src/components/ImageWithSizeCalculation.tsx
index 5db78e0c1276..fe4cc4a01bc0 100644
--- a/src/components/ImageWithSizeCalculation.js
+++ b/src/components/ImageWithSizeCalculation.tsx
@@ -1,31 +1,27 @@
-import PropTypes from 'prop-types';
+import delay from 'lodash/delay';
import React, {useEffect, useRef, useState} from 'react';
-import {View} from 'react-native';
-import _ from 'underscore';
+import {StyleProp, View, ViewStyle} from 'react-native';
+import {OnLoadEvent} from 'react-native-fast-image';
import Log from '@libs/Log';
import styles from '@styles/styles';
import FullscreenLoadingIndicator from './FullscreenLoadingIndicator';
import Image from './Image';
+import RESIZE_MODES from './Image/resizeModes';
-const propTypes = {
+type OnMeasure = (args: {width: number; height: number}) => void;
+
+type ImageWithSizeCalculationProps = {
/** Url for image to display */
- url: PropTypes.string.isRequired,
+ url: string;
/** Any additional styles to apply */
- // eslint-disable-next-line react/forbid-prop-types
- style: PropTypes.any,
+ style?: StyleProp;
/** Callback fired when the image has been measured. */
- onMeasure: PropTypes.func,
+ onMeasure: OnMeasure;
/** Whether the image requires an authToken */
- isAuthTokenRequired: PropTypes.bool,
-};
-
-const defaultProps = {
- style: {},
- onMeasure: () => {},
- isAuthTokenRequired: false,
+ isAuthTokenRequired: boolean;
};
/**
@@ -33,23 +29,19 @@ const defaultProps = {
* Image size must be provided by parent via width and height props. Useful for
* performing some calculation on a network image after fetching dimensions so
* it can be appropriately resized.
- *
- * @param {Object} props
- * @returns {React.Component}
- *
*/
-function ImageWithSizeCalculation(props) {
- const isLoadedRef = useRef(null);
+function ImageWithSizeCalculation({url, style, onMeasure, isAuthTokenRequired}: ImageWithSizeCalculationProps) {
+ const isLoadedRef = useRef(null);
const [isImageCached, setIsImageCached] = useState(true);
const [isLoading, setIsLoading] = useState(false);
const onError = () => {
- Log.hmmm('Unable to fetch image to calculate size', {url: props.url});
+ Log.hmmm('Unable to fetch image to calculate size', {url});
};
- const imageLoadedSuccessfully = (event) => {
+ const imageLoadedSuccessfully = (event: OnLoadEvent) => {
isLoadedRef.current = true;
- props.onMeasure({
+ onMeasure({
width: event.nativeEvent.width,
height: event.nativeEvent.height,
});
@@ -57,10 +49,10 @@ function ImageWithSizeCalculation(props) {
/** Delay the loader to detect whether the image is being loaded from the cache or the internet. */
useEffect(() => {
- if (isLoadedRef.current || !isLoading) {
+ if (isLoadedRef.current ?? !isLoading) {
return;
}
- const timeout = _.delay(() => {
+ const timeout = delay(() => {
if (!isLoading || isLoadedRef.current) {
return;
}
@@ -70,14 +62,14 @@ function ImageWithSizeCalculation(props) {
}, [isLoading]);
return (
-
+
{
- if (isLoadedRef.current || isLoading) {
+ if (isLoadedRef.current ?? isLoading) {
return;
}
setIsLoading(true);
@@ -94,7 +86,5 @@ function ImageWithSizeCalculation(props) {
);
}
-ImageWithSizeCalculation.propTypes = propTypes;
-ImageWithSizeCalculation.defaultProps = defaultProps;
ImageWithSizeCalculation.displayName = 'ImageWithSizeCalculation';
export default React.memo(ImageWithSizeCalculation);
diff --git a/src/components/LHNOptionsList/LHNOptionsList.js b/src/components/LHNOptionsList/LHNOptionsList.js
index 8eb1a06d9505..0c5383054d04 100644
--- a/src/components/LHNOptionsList/LHNOptionsList.js
+++ b/src/components/LHNOptionsList/LHNOptionsList.js
@@ -144,7 +144,7 @@ function LHNOptionsList({
'',
)}`;
const itemComment = draftComments[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`] || '';
- const participantPersonalDetailList = _.values(OptionsListUtils.getPersonalDetailsForAccountIDs(itemFullReport.participantAccountIDs, personalDetails));
+ const participantsPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs(itemFullReport.participantAccountIDs, personalDetails);
return (
{
if (option.accountID) {
- const activeRoute = Navigation.getActiveRoute().replace(/\?.*/, '');
+ const activeRoute = Navigation.getActiveRouteWithoutParams();
Navigation.navigate(ROUTES.PROFILE.getRoute(option.accountID, activeRoute));
} else if (option.reportID) {
diff --git a/src/components/Pressable/GenericPressable/BaseGenericPressable.js b/src/components/Pressable/GenericPressable/BaseGenericPressable.tsx
similarity index 64%
rename from src/components/Pressable/GenericPressable/BaseGenericPressable.js
rename to src/components/Pressable/GenericPressable/BaseGenericPressable.tsx
index d031fdb90ebe..1576fe18da54 100644
--- a/src/components/Pressable/GenericPressable/BaseGenericPressable.js
+++ b/src/components/Pressable/GenericPressable/BaseGenericPressable.tsx
@@ -1,7 +1,6 @@
-import React, {forwardRef, useCallback, useEffect, useMemo} from 'react';
+import React, {ForwardedRef, forwardRef, useCallback, useEffect, useMemo} from 'react';
// eslint-disable-next-line no-restricted-imports
-import {Pressable} from 'react-native';
-import _ from 'underscore';
+import {GestureResponderEvent, Pressable, View, ViewStyle} from 'react-native';
import useSingleExecution from '@hooks/useSingleExecution';
import Accessibility from '@libs/Accessibility';
import HapticFeedback from '@libs/HapticFeedback';
@@ -9,15 +8,12 @@ import KeyboardShortcut from '@libs/KeyboardShortcut';
import styles from '@styles/styles';
import * as StyleUtils from '@styles/StyleUtils';
import CONST from '@src/CONST';
-import genericPressablePropTypes from './PropTypes';
+import PressableProps from './types';
/**
* Returns the cursor style based on the state of Pressable
- * @param {Boolean} isDisabled
- * @param {Boolean} isText
- * @returns {Object}
*/
-const getCursorStyle = (isDisabled, isText) => {
+function getCursorStyle(isDisabled: boolean, isText: boolean): Pick {
if (isDisabled) {
return styles.cursorDisabled;
}
@@ -27,28 +23,34 @@ const getCursorStyle = (isDisabled, isText) => {
}
return styles.cursorPointer;
-};
+}
-const GenericPressable = forwardRef((props, ref) => {
- const {
+function GenericPressable(
+ {
children,
- onPress,
+ onPress = () => {},
onLongPress,
- onKeyPress,
onKeyDown,
disabled,
style,
- shouldUseHapticsOnLongPress,
- shouldUseHapticsOnPress,
+ disabledStyle = {},
+ hoverStyle = {},
+ focusStyle = {},
+ pressStyle = {},
+ screenReaderActiveStyle = {},
+ shouldUseHapticsOnLongPress = false,
+ shouldUseHapticsOnPress = false,
nextFocusRef,
keyboardShortcut,
- shouldUseAutoHitSlop,
- enableInScreenReaderStates,
+ shouldUseAutoHitSlop = false,
+ enableInScreenReaderStates = CONST.SCREEN_READER_STATES.ALL,
onPressIn,
onPressOut,
+ accessible = true,
...rest
- } = props;
-
+ }: PressableProps,
+ ref: ForwardedRef,
+) {
const {isExecuting, singleExecution} = useSingleExecution();
const isScreenReaderActive = Accessibility.useScreenReaderStatus();
const [hitSlop, onLayout] = Accessibility.useAutoHitSlop();
@@ -63,13 +65,14 @@ const GenericPressable = forwardRef((props, ref) => {
shouldBeDisabledByScreenReader = isScreenReaderActive;
}
- return props.disabled || shouldBeDisabledByScreenReader || isExecuting;
- }, [isScreenReaderActive, enableInScreenReaderStates, props.disabled, isExecuting]);
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
+ return disabled || shouldBeDisabledByScreenReader || isExecuting;
+ }, [isScreenReaderActive, enableInScreenReaderStates, disabled, isExecuting]);
const shouldUseDisabledCursor = useMemo(() => isDisabled && !isExecuting, [isDisabled, isExecuting]);
const onLongPressHandler = useCallback(
- (event) => {
+ (event: GestureResponderEvent) => {
if (isDisabled) {
return;
}
@@ -79,8 +82,8 @@ const GenericPressable = forwardRef((props, ref) => {
if (shouldUseHapticsOnLongPress) {
HapticFeedback.longPress();
}
- if (ref && ref.current) {
- ref.current.blur();
+ if (ref && 'current' in ref) {
+ ref.current?.blur();
}
onLongPress(event);
@@ -90,7 +93,7 @@ const GenericPressable = forwardRef((props, ref) => {
);
const onPressHandler = useCallback(
- (event) => {
+ (event?: GestureResponderEvent | KeyboardEvent) => {
if (isDisabled) {
return;
}
@@ -100,8 +103,8 @@ const GenericPressable = forwardRef((props, ref) => {
if (shouldUseHapticsOnPress) {
HapticFeedback.press();
}
- if (ref && ref.current) {
- ref.current.blur();
+ if (ref && 'current' in ref) {
+ ref.current?.blur();
}
onPress(event);
@@ -110,16 +113,6 @@ const GenericPressable = forwardRef((props, ref) => {
[shouldUseHapticsOnPress, onPress, nextFocusRef, ref, isDisabled],
);
- const onKeyPressHandler = useCallback(
- (event) => {
- if (event.key !== 'Enter') {
- return;
- }
- onPressHandler(event);
- },
- [onPressHandler],
- );
-
useEffect(() => {
if (!keyboardShortcut) {
return () => {};
@@ -135,36 +128,37 @@ const GenericPressable = forwardRef((props, ref) => {
ref={ref}
onPress={!isDisabled ? singleExecution(onPressHandler) : undefined}
onLongPress={!isDisabled && onLongPress ? onLongPressHandler : undefined}
- onKeyPress={!isDisabled ? onKeyPressHandler : undefined}
onKeyDown={!isDisabled ? onKeyDown : undefined}
onPressIn={!isDisabled ? onPressIn : undefined}
onPressOut={!isDisabled ? onPressOut : undefined}
style={(state) => [
- getCursorStyle(shouldUseDisabledCursor, [props.role, props.role].includes('text')),
- StyleUtils.parseStyleFromFunction(props.style, state),
- isScreenReaderActive && StyleUtils.parseStyleFromFunction(props.screenReaderActiveStyle, state),
- state.focused && StyleUtils.parseStyleFromFunction(props.focusStyle, state),
- state.hovered && StyleUtils.parseStyleFromFunction(props.hoverStyle, state),
- state.pressed && StyleUtils.parseStyleFromFunction(props.pressStyle, state),
- isDisabled && [...StyleUtils.parseStyleFromFunction(props.disabledStyle, state), styles.noSelect],
+ getCursorStyle(shouldUseDisabledCursor, [rest.accessibilityRole, rest.role].includes('text')),
+ StyleUtils.parseStyleFromFunction(style, state),
+ isScreenReaderActive && StyleUtils.parseStyleFromFunction(screenReaderActiveStyle, state),
+ state.focused && StyleUtils.parseStyleFromFunction(focusStyle, state),
+ state.hovered && StyleUtils.parseStyleFromFunction(hoverStyle, state),
+ state.pressed && StyleUtils.parseStyleFromFunction(pressStyle, state),
+ isDisabled && [StyleUtils.parseStyleFromFunction(disabledStyle, state), styles.noSelect],
]}
// accessibility props
- aria-checked={props.ariaChecked}
+ accessibilityState={{
+ disabled: isDisabled,
+ ...rest.accessibilityState,
+ }}
aria-disabled={isDisabled}
- aria-keyshortcuts={keyboardShortcut && `${keyboardShortcut.modifiers}+${keyboardShortcut.shortcutKey}`}
+ aria-keyshortcuts={keyboardShortcut && `${keyboardShortcut.modifiers.join('')}+${keyboardShortcut.shortcutKey}`}
// ios-only form of inputs
- onMagicTap={!isDisabled && onPressHandler}
- onAccessibilityTap={!isDisabled && onPressHandler}
+ onMagicTap={!isDisabled ? onPressHandler : undefined}
+ onAccessibilityTap={!isDisabled ? onPressHandler : undefined}
+ accessible={accessible}
// eslint-disable-next-line react/jsx-props-no-spreading
{...rest}
>
- {(state) => (_.isFunction(props.children) ? props.children({...state, isScreenReaderActive, isDisabled}) : props.children)}
+ {(state) => (typeof children === 'function' ? children({...state, isScreenReaderActive, isDisabled}) : children)}
);
-});
+}
GenericPressable.displayName = 'GenericPressable';
-GenericPressable.propTypes = genericPressablePropTypes.pressablePropTypes;
-GenericPressable.defaultProps = genericPressablePropTypes.defaultProps;
-export default GenericPressable;
+export default forwardRef(GenericPressable);
diff --git a/src/components/Pressable/GenericPressable/PropTypes.js b/src/components/Pressable/GenericPressable/PropTypes.js
deleted file mode 100644
index 870c63301239..000000000000
--- a/src/components/Pressable/GenericPressable/PropTypes.js
+++ /dev/null
@@ -1,142 +0,0 @@
-import PropTypes from 'prop-types';
-import stylePropType from '@styles/stylePropTypes';
-import CONST from '@src/CONST';
-
-const stylePropTypeWithFunction = PropTypes.oneOfType([stylePropType, PropTypes.func]);
-
-/**
- * Custom test for required props
- * + accessibilityLabel is required when accessible is true
- * @param {Object} props
- * @returns {Error} Error if prop is required
- */
-function requiredPropsCheck(props) {
- if (props.accessible !== true || (props.accessibilityLabel !== undefined && typeof props.accessibilityLabel === 'string')) {
- return;
- }
- return new Error(`Provide a valid string for accessibilityLabel prop when accessible is true`);
-}
-
-const pressablePropTypes = {
- /**
- * onPress callback
- */
- onPress: PropTypes.func,
-
- /**
- * Specifies keyboard shortcut to trigger onPressHandler
- * @example {shortcutKey: 'a', modifiers: ['ctrl', 'shift'], descriptionKey: 'keyboardShortcut.description'}
- */
- keyboardShortcut: PropTypes.shape({
- descriptionKey: PropTypes.string.isRequired,
- shortcutKey: PropTypes.string.isRequired,
- modifiers: PropTypes.arrayOf(PropTypes.string),
- }),
-
- /**
- * Specifies if haptic feedback should be used on press
- * @default false
- */
- shouldUseHapticsOnPress: PropTypes.bool,
-
- /**
- * Specifies if haptic feedback should be used on long press
- * @default false
- */
- shouldUseHapticsOnLongPress: PropTypes.bool,
-
- /**
- * style for when the component is disabled. Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive)
- * @default {}
- * @example {backgroundColor: 'red'}
- * @example state => ({backgroundColor: state.isDisabled ? 'red' : 'blue'})
- */
- disabledStyle: stylePropTypeWithFunction,
-
- /**
- * style for when the component is hovered. Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive)
- * @default {}
- * @example {backgroundColor: 'red'}
- * @example state => ({backgroundColor: state.hovered ? 'red' : 'blue'})
- */
- hoverStyle: stylePropTypeWithFunction,
-
- /**
- * style for when the component is focused. Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive)
- * @default {}
- * @example {backgroundColor: 'red'}
- * @example state => ({backgroundColor: state.focused ? 'red' : 'blue'})
- */
- focusStyle: stylePropTypeWithFunction,
-
- /**
- * style for when the component is pressed. Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive)
- * @default {}
- * @example {backgroundColor: 'red'}
- * @example state => ({backgroundColor: state.pressed ? 'red' : 'blue'})
- */
- pressStyle: stylePropTypeWithFunction,
-
- /**
- * style for when the component is active and the screen reader is on.
- * Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive)
- * @default {}
- * @example {backgroundColor: 'red'}
- * @example state => ({backgroundColor: state.isScreenReaderActive ? 'red' : 'blue'})
- */
- screenReaderActiveStyle: stylePropTypeWithFunction,
-
- /**
- * Specifies if the component should be accessible when the screen reader is on
- * @default 'all'
- * @example 'all' - the component is accessible regardless of screen reader state
- * @example 'active' - the component is accessible only when the screen reader is on
- * @example 'disabled' - the component is not accessible when the screen reader is on
- */
- enableInScreenReaderStates: PropTypes.oneOf([CONST.SCREEN_READER_STATES.ALL, CONST.SCREEN_READER_STATES.ACTIVE, CONST.SCREEN_READER_STATES.DISABLED]),
-
- /**
- * Specifies which component should be focused after interacting with this component
- */
- nextFocusRef: PropTypes.func,
-
- /**
- * Specifies the accessibility label for the component
- * @example 'Search'
- * @example 'Close'
- */
- accessibilityLabel: requiredPropsCheck,
-
- /**
- * Specifies the accessibility hint for the component
- * @example 'Double tap to open'
- */
- accessibilityHint: PropTypes.string,
-
- /**
- * Specifies if the component should calculate its hitSlop automatically
- * @default true
- */
- shouldUseAutoHitSlop: PropTypes.bool,
-};
-
-const defaultProps = {
- onPress: () => {},
- keyboardShortcut: undefined,
- shouldUseHapticsOnPress: false,
- shouldUseHapticsOnLongPress: false,
- disabledStyle: {},
- hoverStyle: {},
- focusStyle: {},
- pressStyle: {},
- screenReaderActiveStyle: {},
- enableInScreenReaderStates: CONST.SCREEN_READER_STATES.ALL,
- nextFocusRef: undefined,
- shouldUseAutoHitSlop: false,
- accessible: true,
-};
-
-export default {
- pressablePropTypes,
- defaultProps,
-};
diff --git a/src/components/Pressable/GenericPressable/index.js b/src/components/Pressable/GenericPressable/index.js
deleted file mode 100644
index 57fc29b8b3f8..000000000000
--- a/src/components/Pressable/GenericPressable/index.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import React, {forwardRef} from 'react';
-import GenericPressable from './BaseGenericPressable';
-import GenericPressablePropTypes from './PropTypes';
-
-const WebGenericPressable = forwardRef((props, ref) => (
-
-));
-
-WebGenericPressable.propTypes = GenericPressablePropTypes.pressablePropTypes;
-WebGenericPressable.defaultProps = GenericPressablePropTypes.defaultProps;
-WebGenericPressable.displayName = 'WebGenericPressable';
-
-export default WebGenericPressable;
diff --git a/src/components/Pressable/GenericPressable/index.native.js b/src/components/Pressable/GenericPressable/index.native.js
deleted file mode 100644
index 407d83bf1eff..000000000000
--- a/src/components/Pressable/GenericPressable/index.native.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import React, {forwardRef} from 'react';
-import GenericPressable from './BaseGenericPressable';
-import GenericPressablePropTypes from './PropTypes';
-
-const NativeGenericPressable = forwardRef((props, ref) => (
-
-));
-
-NativeGenericPressable.propTypes = GenericPressablePropTypes.pressablePropTypes;
-NativeGenericPressable.defaultProps = GenericPressablePropTypes.defaultProps;
-NativeGenericPressable.displayName = 'WebGenericPressable';
-
-export default NativeGenericPressable;
diff --git a/src/components/Pressable/GenericPressable/index.native.tsx b/src/components/Pressable/GenericPressable/index.native.tsx
new file mode 100644
index 000000000000..5bed0f488063
--- /dev/null
+++ b/src/components/Pressable/GenericPressable/index.native.tsx
@@ -0,0 +1,21 @@
+import React, {ForwardedRef, forwardRef} from 'react';
+import {View} from 'react-native';
+import GenericPressable from './BaseGenericPressable';
+import PressableProps from './types';
+
+function NativeGenericPressable(props: PressableProps, ref: ForwardedRef) {
+ return (
+
+ );
+}
+
+NativeGenericPressable.displayName = 'NativeGenericPressable';
+
+export default forwardRef(NativeGenericPressable);
diff --git a/src/components/Pressable/GenericPressable/index.tsx b/src/components/Pressable/GenericPressable/index.tsx
new file mode 100644
index 000000000000..c8e9560062e0
--- /dev/null
+++ b/src/components/Pressable/GenericPressable/index.tsx
@@ -0,0 +1,30 @@
+import React, {ForwardedRef, forwardRef} from 'react';
+import {Role, View} from 'react-native';
+import GenericPressable from './BaseGenericPressable';
+import PressableProps from './types';
+
+function WebGenericPressable(props: PressableProps, ref: ForwardedRef) {
+ return (
+
+ );
+}
+
+WebGenericPressable.displayName = 'WebGenericPressable';
+
+export default forwardRef(WebGenericPressable);
diff --git a/src/components/Pressable/GenericPressable/types.ts b/src/components/Pressable/GenericPressable/types.ts
new file mode 100644
index 000000000000..35616cb600a3
--- /dev/null
+++ b/src/components/Pressable/GenericPressable/types.ts
@@ -0,0 +1,147 @@
+import {ElementRef, RefObject} from 'react';
+import {GestureResponderEvent, HostComponent, PressableStateCallbackType, PressableProps as RNPressableProps, StyleProp, ViewStyle} from 'react-native';
+import {ValueOf} from 'type-fest';
+import CONST from '@src/CONST';
+
+type StylePropWithFunction = StyleProp | ((state: PressableStateCallbackType) => StyleProp);
+
+type Shortcut = {
+ displayName: string;
+ shortcutKey: string;
+ descriptionKey: string;
+ modifiers: string[];
+};
+
+type RequiredAccessibilityLabel =
+ | {
+ /**
+ * When true, indicates that the view is an accessibility element.
+ * By default, all the touchable elements are accessible.
+ */
+ accessible?: true | undefined;
+
+ /**
+ * Specifies the accessibility label for the component
+ * @example 'Search'
+ * @example 'Close'
+ */
+ accessibilityLabel: string;
+ }
+ | {
+ /**
+ * When false, indicates that the view is not an accessibility element.
+ */
+ accessible: false;
+
+ /**
+ * Specifies the accessibility label for the component
+ * @example 'Search'
+ * @example 'Close'
+ */
+ accessibilityLabel?: string;
+ };
+
+type PressableProps = RNPressableProps &
+ RequiredAccessibilityLabel & {
+ /**
+ * onPress callback
+ */
+ onPress: (event?: GestureResponderEvent | KeyboardEvent) => void;
+
+ /**
+ * Specifies keyboard shortcut to trigger onPressHandler
+ * @example {shortcutKey: 'a', modifiers: ['ctrl', 'shift'], descriptionKey: 'keyboardShortcut.description'}
+ */
+ keyboardShortcut?: Shortcut;
+
+ /**
+ * Specifies if haptic feedback should be used on press
+ * @default false
+ */
+ shouldUseHapticsOnPress?: boolean;
+
+ /**
+ * Specifies if haptic feedback should be used on long press
+ * @default false
+ */
+ shouldUseHapticsOnLongPress?: boolean;
+
+ /**
+ * style for when the component is disabled. Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive)
+ * @default {}
+ * @example {backgroundColor: 'red'}
+ * @example state => ({backgroundColor: state.isDisabled ? 'red' : 'blue'})
+ */
+ disabledStyle?: StylePropWithFunction;
+
+ /**
+ * style for when the component is hovered. Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive)
+ * @default {}
+ * @example {backgroundColor: 'red'}
+ * @example state => ({backgroundColor: state.hovered ? 'red' : 'blue'})
+ */
+ hoverStyle?: StylePropWithFunction;
+
+ /**
+ * style for when the component is focused. Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive)
+ * @default {}
+ * @example {backgroundColor: 'red'}
+ * @example state => ({backgroundColor: state.focused ? 'red' : 'blue'})
+ */
+ focusStyle?: StylePropWithFunction;
+
+ /**
+ * style for when the component is pressed. Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive)
+ * @default {}
+ * @example {backgroundColor: 'red'}
+ * @example state => ({backgroundColor: state.pressed ? 'red' : 'blue'})
+ */
+ pressStyle?: StylePropWithFunction;
+
+ /**
+ * style for when the component is active and the screen reader is on.
+ * Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive)
+ * @default {}
+ * @example {backgroundColor: 'red'}
+ * @example state => ({backgroundColor: state.isScreenReaderActive ? 'red' : 'blue'})
+ */
+ screenReaderActiveStyle?: StylePropWithFunction;
+
+ /**
+ * Specifies if the component should be accessible when the screen reader is on
+ * @default 'all'
+ * @example 'all' - the component is accessible regardless of screen reader state
+ * @example 'active' - the component is accessible only when the screen reader is on
+ * @example 'disabled' - the component is not accessible when the screen reader is on
+ */
+ enableInScreenReaderStates?: ValueOf;
+
+ /**
+ * Specifies which component should be focused after interacting with this component
+ */
+ nextFocusRef?: ElementRef> & RefObject;
+
+ /**
+ * Specifies the accessibility label for the component
+ * @example 'Search'
+ * @example 'Close'
+ */
+ accessibilityLabel?: string;
+
+ /**
+ * Specifies the accessibility hint for the component
+ * @example 'Double tap to open'
+ */
+ accessibilityHint?: string;
+
+ /**
+ * Specifies if the component should calculate its hitSlop automatically
+ * @default true
+ */
+ shouldUseAutoHitSlop?: boolean;
+
+ /** Turns off drag area for the component */
+ noDragArea?: boolean;
+ };
+
+export default PressableProps;
diff --git a/src/components/Pressable/PressableWithDelayToggle.js b/src/components/Pressable/PressableWithDelayToggle.tsx
similarity index 51%
rename from src/components/Pressable/PressableWithDelayToggle.js
rename to src/components/Pressable/PressableWithDelayToggle.tsx
index 1b5da3dca38c..c402710d71bd 100644
--- a/src/components/Pressable/PressableWithDelayToggle.js
+++ b/src/components/Pressable/PressableWithDelayToggle.tsx
@@ -1,8 +1,9 @@
-import PropTypes from 'prop-types';
-import React from 'react';
+/* eslint-disable react-native-a11y/has-valid-accessibility-descriptors */
+import React, {ForwardedRef, forwardRef} from 'react';
+import {Text as RNText, StyleProp, TextStyle, View, ViewStyle} from 'react-native';
+import {SvgProps} from 'react-native-svg';
import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
-import refPropTypes from '@components/refPropTypes';
import Text from '@components/Text';
import Tooltip from '@components/Tooltip';
import useThrottledButtonState from '@hooks/useThrottledButtonState';
@@ -10,68 +11,61 @@ import getButtonState from '@libs/getButtonState';
import styles from '@styles/styles';
import * as StyleUtils from '@styles/StyleUtils';
import variables from '@styles/variables';
+import PressableProps from './GenericPressable/types';
import PressableWithoutFeedback from './PressableWithoutFeedback';
-const propTypes = {
- /** Ref passed to the component by React.forwardRef (do not pass from parent) */
- innerRef: refPropTypes,
-
+type PressableWithDelayToggleProps = PressableProps & {
/** The text to display */
- text: PropTypes.string,
+ text: string;
/** The text to display once the pressable is pressed */
- textChecked: PropTypes.string,
+ textChecked: string;
/** The tooltip text to display */
- tooltipText: PropTypes.string,
+ tooltipText: string;
/** The tooltip text to display once the pressable is pressed */
- tooltipTextChecked: PropTypes.string,
+ tooltipTextChecked: string;
/** Styles to apply to the container */
- // eslint-disable-next-line react/forbid-prop-types
- styles: PropTypes.arrayOf(PropTypes.object),
+ styles?: StyleProp;
- /** Styles to apply to the text */
- // eslint-disable-next-line react/forbid-prop-types
- textStyles: PropTypes.arrayOf(PropTypes.object),
+ // /** Styles to apply to the text */
+ textStyles?: StyleProp;
/** Styles to apply to the icon */
- // eslint-disable-next-line react/forbid-prop-types
- iconStyles: PropTypes.arrayOf(PropTypes.object),
-
- /** Callback to be called on onPress */
- onPress: PropTypes.func.isRequired,
+ iconStyles?: StyleProp;
/** The icon to display */
- icon: PropTypes.func,
+ icon?: React.FC;
/** The icon to display once the pressable is pressed */
- iconChecked: PropTypes.func,
+ iconChecked?: React.FC;
/**
* Should be set to `true` if this component is being rendered inline in
* another `Text`. This is due to limitations in RN regarding the
* vertical text alignment of non-Text elements
*/
- inline: PropTypes.bool,
-};
-
-const defaultProps = {
- text: '',
- textChecked: '',
- tooltipText: '',
- tooltipTextChecked: '',
- styles: [],
- textStyles: [],
- iconStyles: [],
- icon: null,
- inline: true,
- iconChecked: Expensicons.Checkmark,
- innerRef: () => {},
+ inline?: boolean;
};
-function PressableWithDelayToggle(props) {
+function PressableWithDelayToggle(
+ {
+ iconChecked = Expensicons.Checkmark,
+ inline = true,
+ onPress,
+ text,
+ textChecked,
+ tooltipText,
+ tooltipTextChecked,
+ styles: pressableStyle,
+ textStyles,
+ iconStyles,
+ icon,
+ }: PressableWithDelayToggleProps,
+ ref: ForwardedRef,
+) {
const [isActive, temporarilyDisableInteractions] = useThrottledButtonState();
const updatePressState = () => {
@@ -79,54 +73,57 @@ function PressableWithDelayToggle(props) {
return;
}
temporarilyDisableInteractions();
- props.onPress();
+ onPress();
};
// Due to limitations in RN regarding the vertical text alignment of non-Text elements,
// for elements that are supposed to be inline, we need to use a Text element instead
// of a Pressable
- const PressableView = props.inline ? Text : PressableWithoutFeedback;
- const tooltipText = !isActive ? props.tooltipTextChecked : props.tooltipText;
+ const PressableView = inline ? Text : PressableWithoutFeedback;
+ const tooltipTexts = !isActive ? tooltipTextChecked : tooltipText;
const labelText = (
- {!isActive && props.textChecked ? props.textChecked : props.text}
+ {!isActive && textChecked ? textChecked : text}
);
return (
<>
- {props.inline && labelText}
+ {inline && labelText}
{({hovered, pressed}) => (
<>
- {!props.inline && labelText}
- {props.icon && (
+ {!inline && labelText}
+ {icon && (
)}
>
@@ -138,18 +135,6 @@ function PressableWithDelayToggle(props) {
);
}
-PressableWithDelayToggle.propTypes = propTypes;
-PressableWithDelayToggle.defaultProps = defaultProps;
PressableWithDelayToggle.displayName = 'PressableWithDelayToggle';
-const PressableWithDelayToggleWithRef = React.forwardRef((props, ref) => (
-
-));
-
-PressableWithDelayToggleWithRef.displayName = 'PressableWithDelayToggleWithRef';
-
-export default PressableWithDelayToggleWithRef;
+export default forwardRef(PressableWithDelayToggle);
diff --git a/src/components/Pressable/PressableWithFeedback.js b/src/components/Pressable/PressableWithFeedback.js
deleted file mode 100644
index 2ef0ddeb5483..000000000000
--- a/src/components/Pressable/PressableWithFeedback.js
+++ /dev/null
@@ -1,95 +0,0 @@
-import propTypes from 'prop-types';
-import React, {forwardRef, useState} from 'react';
-import _ from 'underscore';
-import OpacityView from '@components/OpacityView';
-import variables from '@styles/variables';
-import GenericPressable from './GenericPressable';
-import GenericPressablePropTypes from './GenericPressable/PropTypes';
-
-const omittedProps = ['wrapperStyle', 'needsOffscreenAlphaCompositing'];
-
-const PressableWithFeedbackPropTypes = {
- ...GenericPressablePropTypes.pressablePropTypes,
- /**
- * Determines what opacity value should be applied to the underlaying view when Pressable is pressed.
- * To disable dimming, pass 1 as pressDimmingValue
- * @default variables.pressDimValue
- */
- pressDimmingValue: propTypes.number,
- /**
- * Determines what opacity value should be applied to the underlaying view when pressable is hovered.
- * To disable dimming, pass 1 as hoverDimmingValue
- * @default variables.hoverDimValue
- */
- hoverDimmingValue: propTypes.number,
- /**
- * Used to locate this view from native classes.
- */
- id: propTypes.string,
-
- /** Whether the view needs to be rendered offscreen (for Android only) */
- needsOffscreenAlphaCompositing: propTypes.bool,
-};
-
-const PressableWithFeedbackDefaultProps = {
- ...GenericPressablePropTypes.defaultProps,
- pressDimmingValue: variables.pressDimValue,
- hoverDimmingValue: variables.hoverDimValue,
- id: '',
- wrapperStyle: [],
- needsOffscreenAlphaCompositing: false,
-};
-
-const PressableWithFeedback = forwardRef((props, ref) => {
- const propsWithoutWrapperProps = _.omit(props, omittedProps);
- const [isPressed, setIsPressed] = useState(false);
- const [isHovered, setIsHovered] = useState(false);
-
- return (
-
- {
- setIsHovered(true);
- if (props.onHoverIn) {
- props.onHoverIn();
- }
- }}
- onHoverOut={() => {
- setIsHovered(false);
- if (props.onHoverOut) {
- props.onHoverOut();
- }
- }}
- onPressIn={() => {
- setIsPressed(true);
- if (props.onPressIn) {
- props.onPressIn();
- }
- }}
- onPressOut={() => {
- setIsPressed(false);
- if (props.onPressOut) {
- props.onPressOut();
- }
- }}
- >
- {(state) => (_.isFunction(props.children) ? props.children(state) : props.children)}
-
-
- );
-});
-
-PressableWithFeedback.displayName = 'PressableWithFeedback';
-PressableWithFeedback.propTypes = PressableWithFeedbackPropTypes;
-PressableWithFeedback.defaultProps = PressableWithFeedbackDefaultProps;
-
-export default PressableWithFeedback;
diff --git a/src/components/Pressable/PressableWithFeedback.tsx b/src/components/Pressable/PressableWithFeedback.tsx
new file mode 100644
index 000000000000..5d7f7c110ea7
--- /dev/null
+++ b/src/components/Pressable/PressableWithFeedback.tsx
@@ -0,0 +1,90 @@
+import React, {ForwardedRef, forwardRef, useState} from 'react';
+import {StyleProp, View, ViewStyle} from 'react-native';
+import {AnimatedStyle} from 'react-native-reanimated';
+import OpacityView from '@components/OpacityView';
+import variables from '@styles/variables';
+import GenericPressable from './GenericPressable';
+import PressableProps from './GenericPressable/types';
+
+type PressableWithFeedbackProps = PressableProps & {
+ /** Style for the wrapper view */
+ wrapperStyle?: StyleProp>;
+
+ /**
+ * Determines what opacity value should be applied to the underlaying view when Pressable is pressed.
+ * To disable dimming, pass 1 as pressDimmingValue
+ * @default variables.pressDimValue
+ */
+ pressDimmingValue?: number;
+
+ /**
+ * Determines what opacity value should be applied to the underlaying view when pressable is hovered.
+ * To disable dimming, pass 1 as hoverDimmingValue
+ * @default variables.hoverDimValue
+ */
+ hoverDimmingValue?: number;
+
+ /** Whether the view needs to be rendered offscreen (for Android only) */
+ needsOffscreenAlphaCompositing?: boolean;
+};
+
+function PressableWithFeedback(
+ {
+ children,
+ wrapperStyle = [],
+ needsOffscreenAlphaCompositing = false,
+ pressDimmingValue = variables.pressDimValue,
+ hoverDimmingValue = variables.hoverDimValue,
+ ...rest
+ }: PressableWithFeedbackProps,
+ ref: ForwardedRef,
+) {
+ const [isPressed, setIsPressed] = useState(false);
+ const [isHovered, setIsHovered] = useState(false);
+
+ return (
+
+ {
+ setIsHovered(true);
+ if (rest.onHoverIn) {
+ rest.onHoverIn(event);
+ }
+ }}
+ onHoverOut={(event) => {
+ setIsHovered(false);
+ if (rest.onHoverOut) {
+ rest.onHoverOut(event);
+ }
+ }}
+ onPressIn={(event) => {
+ setIsPressed(true);
+ if (rest.onPressIn) {
+ rest.onPressIn(event);
+ }
+ }}
+ onPressOut={(event) => {
+ setIsPressed(false);
+ if (rest.onPressOut) {
+ rest.onPressOut(event);
+ }
+ }}
+ >
+ {(state) => (typeof children === 'function' ? children(state) : children)}
+
+
+ );
+}
+
+PressableWithFeedback.displayName = 'PressableWithFeedback';
+
+export default forwardRef(PressableWithFeedback);
diff --git a/src/components/Pressable/PressableWithoutFeedback.js b/src/components/Pressable/PressableWithoutFeedback.js
deleted file mode 100644
index 92e704550dec..000000000000
--- a/src/components/Pressable/PressableWithoutFeedback.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import React from 'react';
-import _ from 'underscore';
-import GenericPressable from './GenericPressable';
-import GenericPressableProps from './GenericPressable/PropTypes';
-
-const omittedProps = ['pressStyle', 'hoverStyle', 'focusStyle', 'activeStyle', 'disabledStyle', 'screenReaderActiveStyle', 'shouldUseHapticsOnPress', 'shouldUseHapticsOnLongPress'];
-
-const PressableWithoutFeedback = React.forwardRef((props, ref) => {
- const propsWithoutStyling = _.omit(props, omittedProps);
- return (
-
- );
-});
-
-PressableWithoutFeedback.displayName = 'PressableWithoutFeedback';
-PressableWithoutFeedback.propTypes = _.omit(GenericPressableProps.pressablePropTypes, omittedProps);
-PressableWithoutFeedback.defaultProps = _.omit(GenericPressableProps.defaultProps, omittedProps);
-
-export default PressableWithoutFeedback;
diff --git a/src/components/Pressable/PressableWithoutFeedback.tsx b/src/components/Pressable/PressableWithoutFeedback.tsx
new file mode 100644
index 000000000000..c3b780e63cfd
--- /dev/null
+++ b/src/components/Pressable/PressableWithoutFeedback.tsx
@@ -0,0 +1,21 @@
+import React, {ForwardedRef} from 'react';
+import {View} from 'react-native';
+import GenericPressable from './GenericPressable';
+import PressableProps from './GenericPressable/types';
+
+function PressableWithoutFeedback(
+ {pressStyle, hoverStyle, focusStyle, disabledStyle, screenReaderActiveStyle, shouldUseHapticsOnPress, shouldUseHapticsOnLongPress, ...rest}: PressableProps,
+ ref: ForwardedRef,
+) {
+ return (
+
+ );
+}
+
+PressableWithoutFeedback.displayName = 'PressableWithoutFeedback';
+
+export default React.forwardRef(PressableWithoutFeedback);
diff --git a/src/components/Pressable/PressableWithoutFocus.js b/src/components/Pressable/PressableWithoutFocus.js
deleted file mode 100644
index 641e695b1013..000000000000
--- a/src/components/Pressable/PressableWithoutFocus.js
+++ /dev/null
@@ -1,68 +0,0 @@
-import PropTypes from 'prop-types';
-import React from 'react';
-import _ from 'underscore';
-import StylePropType from '@styles/stylePropTypes';
-import GenericPressable from './GenericPressable';
-import genericPressablePropTypes from './GenericPressable/PropTypes';
-
-const propTypes = {
- /** Element that should be clickable */
- children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired,
-
- /** Callback for onPress event */
- onPress: PropTypes.func.isRequired,
-
- /** Callback for onLongPress event */
- onLongPress: PropTypes.func,
-
- /** Styles that should be passed to touchable container */
- style: StylePropType,
-
- /** Proptypes of pressable component used for implementation */
- ...genericPressablePropTypes.pressablePropTypes,
-};
-
-const defaultProps = {
- style: [],
- onLongPress: undefined,
-};
-
-/**
- * This component prevents the tapped element from capturing focus.
- * We need to blur this element when clicked as it opens modal that implements focus-trapping.
- * When the modal is closed it focuses back to the last active element.
- * Therefore it shifts the element to bring it back to focus.
- * https://github.com/Expensify/App/issues/6806
- */
-class PressableWithoutFocus extends React.Component {
- constructor(props) {
- super(props);
- this.pressAndBlur = this.pressAndBlur.bind(this);
- }
-
- pressAndBlur() {
- this.pressableRef.blur();
- this.props.onPress();
- }
-
- render() {
- const restProps = _.omit(this.props, ['children', 'onPress', 'onLongPress', 'style']);
- return (
- (this.pressableRef = el)}
- style={this.props.style}
- // eslint-disable-next-line react/jsx-props-no-spreading
- {...restProps}
- >
- {this.props.children}
-
- );
- }
-}
-
-PressableWithoutFocus.propTypes = propTypes;
-PressableWithoutFocus.defaultProps = defaultProps;
-
-export default PressableWithoutFocus;
diff --git a/src/components/Pressable/PressableWithoutFocus.tsx b/src/components/Pressable/PressableWithoutFocus.tsx
new file mode 100644
index 000000000000..32cb1708baf0
--- /dev/null
+++ b/src/components/Pressable/PressableWithoutFocus.tsx
@@ -0,0 +1,36 @@
+import React, {useRef} from 'react';
+import {View} from 'react-native';
+import GenericPressable from './GenericPressable';
+import PressableProps from './GenericPressable/types';
+
+/**
+ * This component prevents the tapped element from capturing focus.
+ * We need to blur this element when clicked as it opens modal that implements focus-trapping.
+ * When the modal is closed it focuses back to the last active element.
+ * Therefore it shifts the element to bring it back to focus.
+ * https://github.com/Expensify/App/issues/6806
+ */
+function PressableWithoutFocus({children, onPress, onLongPress, ...rest}: PressableProps) {
+ const ref = useRef(null);
+
+ const pressAndBlur = () => {
+ ref?.current?.blur();
+ onPress();
+ };
+
+ return (
+
+ {children}
+
+ );
+}
+
+PressableWithoutFocus.displayName = 'PressableWithoutFocus';
+
+export default PressableWithoutFocus;
diff --git a/src/components/Pressable/index.js b/src/components/Pressable/index.ts
similarity index 100%
rename from src/components/Pressable/index.js
rename to src/components/Pressable/index.ts
diff --git a/src/components/ReportActionItem/TaskView.js b/src/components/ReportActionItem/TaskView.js
index 9aa85392dde7..b12d6ae32128 100644
--- a/src/components/ReportActionItem/TaskView.js
+++ b/src/components/ReportActionItem/TaskView.js
@@ -58,8 +58,8 @@ function TaskView(props) {
Task.clearEditTaskErrors(props.report.reportID)}
+ errors={lodashGet(props, 'report.errorFields.editTask') || lodashGet(props, 'report.errorFields.createTask')}
+ onClose={() => Task.clearTaskErrors(props.report.reportID)}
errorRowStyles={styles.ph5}
>
diff --git a/src/hooks/useSingleExecution.ts b/src/hooks/useSingleExecution.ts
new file mode 100644
index 000000000000..16a98152def1
--- /dev/null
+++ b/src/hooks/useSingleExecution.ts
@@ -0,0 +1,40 @@
+import {useCallback, useRef, useState} from 'react';
+import {InteractionManager} from 'react-native';
+
+type Action = (...params: T) => void | Promise;
+
+/**
+ * With any action passed in, it will only allow 1 such action to occur at a time.
+ */
+export default function useSingleExecution() {
+ const [isExecuting, setIsExecuting] = useState(false);
+ const isExecutingRef = useRef();
+
+ isExecutingRef.current = isExecuting;
+
+ const singleExecution = useCallback(
+ (action: Action) =>
+ (...params: T) => {
+ if (isExecutingRef.current) {
+ return;
+ }
+
+ setIsExecuting(true);
+ isExecutingRef.current = true;
+
+ const execution = action(...params);
+ InteractionManager.runAfterInteractions(() => {
+ if (!(execution instanceof Promise)) {
+ setIsExecuting(false);
+ return;
+ }
+ execution.finally(() => {
+ setIsExecuting(false);
+ });
+ });
+ },
+ [],
+ );
+
+ return {isExecuting, singleExecution};
+}
diff --git a/src/hooks/useSingleExecution/index.native.js b/src/hooks/useSingleExecution/index.native.js
index a2b4ccb4cd53..16a98152def1 100644
--- a/src/hooks/useSingleExecution/index.native.js
+++ b/src/hooks/useSingleExecution/index.native.js
@@ -1,20 +1,20 @@
import {useCallback, useRef, useState} from 'react';
import {InteractionManager} from 'react-native';
+type Action = (...params: T) => void | Promise;
+
/**
* With any action passed in, it will only allow 1 such action to occur at a time.
- *
- * @returns {Object}
*/
export default function useSingleExecution() {
const [isExecuting, setIsExecuting] = useState(false);
- const isExecutingRef = useRef();
+ const isExecutingRef = useRef();
isExecutingRef.current = isExecuting;
const singleExecution = useCallback(
- (action) =>
- (...params) => {
+ (action: Action) =>
+ (...params: T) => {
if (isExecutingRef.current) {
return;
}
diff --git a/src/languages/en.ts b/src/languages/en.ts
index 8ce15d16f4fe..38efe0ef92f6 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -1637,6 +1637,7 @@ export default {
markAsComplete: 'Mark as complete',
markAsIncomplete: 'Mark as incomplete',
assigneeError: 'There was an error assigning this task, please try another assignee.',
+ genericCreateTaskFailureMessage: 'Unexpected error create task, please try again later.',
},
statementPage: {
generatingPDF: "We're generating your PDF right now. Please come back later!",
diff --git a/src/languages/es.ts b/src/languages/es.ts
index 9d0184ffff30..2bdb71ae82f7 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -1660,6 +1660,7 @@ export default {
markAsComplete: 'Marcar como completada',
markAsIncomplete: 'Marcar como incompleta',
assigneeError: 'Hubo un error al asignar esta tarea, inténtalo con otro usuario.',
+ genericCreateTaskFailureMessage: 'Error inesperado al crear el tarea, por favor, inténtalo más tarde.',
},
statementPage: {
generatingPDF: 'Estamos generando tu PDF ahora mismo. ¡Por favor, vuelve más tarde!',
diff --git a/src/libs/Accessibility/index.ts b/src/libs/Accessibility/index.ts
index 5eceda8edcb1..aa167b1239b2 100644
--- a/src/libs/Accessibility/index.ts
+++ b/src/libs/Accessibility/index.ts
@@ -42,7 +42,7 @@ const useAutoHitSlop = () => {
},
[frameSize],
);
- return [getHitSlopForSize(frameSize), onLayout];
+ return [getHitSlopForSize(frameSize), onLayout] as const;
};
export default {
diff --git a/src/libs/KeyboardShortcut/index.ts b/src/libs/KeyboardShortcut/index.ts
index cfcf5d5ef535..1b684a7ab19f 100644
--- a/src/libs/KeyboardShortcut/index.ts
+++ b/src/libs/KeyboardShortcut/index.ts
@@ -128,7 +128,7 @@ function getPlatformEquivalentForKeys(keys: string[]): string[] {
*/
function subscribe(
key: string,
- callback: () => void,
+ callback: (event?: KeyboardEvent) => void,
descriptionKey: string,
modifiers: string[] = ['shift'],
captureOnInputs = false,
diff --git a/src/libs/Navigation/Navigation.js b/src/libs/Navigation/Navigation.js
index ae13e2b07206..de6c4a64237b 100644
--- a/src/libs/Navigation/Navigation.js
+++ b/src/libs/Navigation/Navigation.js
@@ -96,8 +96,8 @@ function navigate(route = ROUTES.HOME, type) {
/**
* @param {String} fallbackRoute - Fallback route if pop/goBack action should, but is not possible within RHP
- * @param {Bool} shouldEnforceFallback - Enforces navigation to fallback route
- * @param {Bool} shouldPopToTop - Should we navigate to LHN on back press
+ * @param {Boolean} shouldEnforceFallback - Enforces navigation to fallback route
+ * @param {Boolean} shouldPopToTop - Should we navigate to LHN on back press
*/
function goBack(fallbackRoute, shouldEnforceFallback = false, shouldPopToTop = false) {
if (!canNavigate('goBack')) {
@@ -207,6 +207,14 @@ function getActiveRoute() {
return '';
}
+/**
+ * Returns the current active route without the URL params
+ * @returns {String}
+ */
+function getActiveRouteWithoutParams() {
+ return getActiveRoute().replace(/\?.*/, '');
+}
+
/** Returns the active route name from a state event from the navigationRef
* @param {Object} event
* @returns {String | undefined}
@@ -270,6 +278,7 @@ export default {
dismissModal,
isActiveRoute,
getActiveRoute,
+ getActiveRouteWithoutParams,
goBack,
isNavigationReady,
setIsNavigationReady,
diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js
index b2db1758f24b..c017e6c7664e 100644
--- a/src/libs/Navigation/linkingConfig.js
+++ b/src/libs/Navigation/linkingConfig.js
@@ -142,7 +142,7 @@ export default {
exact: true,
},
Settings_ContactMethods: {
- path: ROUTES.SETTINGS_CONTACT_METHODS,
+ path: ROUTES.SETTINGS_CONTACT_METHODS.route,
exact: true,
},
Settings_ContactMethodDetails: {
diff --git a/src/libs/actions/ReportActions.ts b/src/libs/actions/ReportActions.ts
index b9cea498a3fa..d7ff96fc6c2e 100644
--- a/src/libs/actions/ReportActions.ts
+++ b/src/libs/actions/ReportActions.ts
@@ -4,6 +4,7 @@ import * as ReportUtils from '@libs/ReportUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ReportAction from '@src/types/onyx/ReportAction';
+import * as Report from './Report';
function clearReportActionErrors(reportID: string, reportAction: ReportAction) {
const originalReportID = ReportUtils.getOriginalReportID(reportID, reportAction);
@@ -24,6 +25,11 @@ function clearReportActionErrors(reportID: string, reportAction: ReportAction) {
Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION}${linkedTransactionID}`, null);
}
+ // Delete the failed task report too
+ const taskReportID = reportAction.message?.[0]?.taskReportID;
+ if (taskReportID) {
+ Report.deleteReport(taskReportID);
+ }
return;
}
diff --git a/src/libs/actions/Task.js b/src/libs/actions/Task.js
index 959710967881..e884a4d7a6b3 100644
--- a/src/libs/actions/Task.js
+++ b/src/libs/actions/Task.js
@@ -15,6 +15,7 @@ import * as UserUtils from '@libs/UserUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
+import * as Report from './Report';
let currentUserEmail;
let currentUserAccountID;
@@ -134,9 +135,13 @@ function createTaskAndNavigate(parentReportID, title, description, assigneeEmail
// FOR TASK REPORT
const failureData = [
{
- onyxMethod: Onyx.METHOD.SET,
+ onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT}${optimisticTaskReport.reportID}`,
- value: null,
+ value: {
+ errorFields: {
+ createTask: ErrorUtils.getMicroSecondOnyxError('task.genericCreateTaskFailureMessage'),
+ },
+ },
},
{
onyxMethod: Onyx.METHOD.MERGE,
@@ -186,7 +191,11 @@ function createTaskAndNavigate(parentReportID, title, description, assigneeEmail
failureData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${parentReportID}`,
- value: {[optimisticAddCommentReport.reportAction.reportActionID]: {pendingAction: null}},
+ value: {
+ [optimisticAddCommentReport.reportAction.reportActionID]: {
+ errors: ErrorUtils.getMicroSecondOnyxError('task.genericCreateTaskFailureMessage'),
+ },
+ },
});
clearOutTaskInfo();
@@ -879,7 +888,19 @@ function canModifyTask(taskReport, sessionAccountID) {
/**
* @param {String} reportID
*/
-function clearEditTaskErrors(reportID) {
+function clearTaskErrors(reportID) {
+ const report = ReportUtils.getReport(reportID);
+
+ // Delete the task preview in the parent report
+ if (lodashGet(report, 'pendingFields.createChat') === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD) {
+ Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.parentReportID}`, {
+ [report.parentReportActionID]: null,
+ });
+
+ Report.navigateToConciergeChatAndDeleteReport(reportID);
+ return;
+ }
+
Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {
pendingFields: null,
errorFields: null,
@@ -934,7 +955,7 @@ export {
cancelTask,
dismissModalAndClearOutTaskInfo,
getTaskAssigneeAccountID,
- clearEditTaskErrors,
+ clearTaskErrors,
canModifyTask,
getTaskReportActionMessage,
};
diff --git a/src/libs/actions/User.js b/src/libs/actions/User.js
index f7375a5583a6..3c91dc4624cd 100644
--- a/src/libs/actions/User.js
+++ b/src/libs/actions/User.js
@@ -238,7 +238,7 @@ function deleteContactMethod(contactMethod, loginList) {
},
{optimisticData, successData, failureData},
);
- Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS);
+ Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS.route);
}
/**
@@ -328,7 +328,7 @@ function addNewContactMethodAndNavigate(contactMethod) {
];
API.write('AddNewContactMethod', {partnerUserID: contactMethod}, {optimisticData, successData, failureData});
- Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS);
+ Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS.route);
}
/**
@@ -755,7 +755,7 @@ function setContactMethodAsDefault(newDefaultContactMethod) {
},
];
API.write('SetContactMethodAsDefault', {partnerUserID: newDefaultContactMethod}, {optimisticData, successData, failureData});
- Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS);
+ Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS.route);
}
/**
diff --git a/src/libs/getButtonState.ts b/src/libs/getButtonState.ts
index 6b89e1b7d383..fe593b9f613e 100644
--- a/src/libs/getButtonState.ts
+++ b/src/libs/getButtonState.ts
@@ -1,12 +1,10 @@
import {ValueOf} from 'type-fest';
import CONST from '@src/CONST';
-type GetButtonState = (isActive: boolean, isPressed: boolean, isComplete: boolean, isDisabled: boolean, isInteractive: boolean) => ValueOf;
-
/**
* Get the string representation of a button's state.
*/
-const getButtonState: GetButtonState = (isActive = false, isPressed = false, isComplete = false, isDisabled = false, isInteractive = true) => {
+function getButtonState(isActive = false, isPressed = false, isComplete = false, isDisabled = false, isInteractive = true): ValueOf {
if (!isInteractive) {
return CONST.BUTTON_STATES.DEFAULT;
}
@@ -28,6 +26,6 @@ const getButtonState: GetButtonState = (isActive = false, isPressed = false, isC
}
return CONST.BUTTON_STATES.DEFAULT;
-};
+}
export default getButtonState;
diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js
index 302b7d35a1c9..c958189d68b5 100644
--- a/src/pages/EditRequestPage.js
+++ b/src/pages/EditRequestPage.js
@@ -184,7 +184,7 @@ function EditRequestPage({betas, report, route, parentReport, policyCategories,
});
}}
onNavigateToCurrency={() => {
- const activeRoute = encodeURIComponent(Navigation.getActiveRoute().replace(/\?.*/, ''));
+ const activeRoute = encodeURIComponent(Navigation.getActiveRouteWithoutParams());
Navigation.navigate(ROUTES.EDIT_CURRENCY_REQUEST.getRoute(report.reportID, defaultCurrency, activeRoute));
}}
/>
diff --git a/src/pages/EditSplitBillPage.js b/src/pages/EditSplitBillPage.js
index 1342d9297d3e..c4e47e2d4c35 100644
--- a/src/pages/EditSplitBillPage.js
+++ b/src/pages/EditSplitBillPage.js
@@ -112,7 +112,7 @@ function EditSplitBillPage({route, transaction, draftTransaction}) {
});
}}
onNavigateToCurrency={() => {
- const activeRoute = encodeURIComponent(Navigation.getActiveRoute().replace(/\?.*/, ''));
+ const activeRoute = encodeURIComponent(Navigation.getActiveRouteWithoutParams());
Navigation.navigate(ROUTES.EDIT_SPLIT_BILL_CURRENCY.getRoute(reportID, reportActionID, defaultCurrency, activeRoute));
}}
/>
diff --git a/src/pages/TeachersUnite/ImTeacherUpdateEmailPage.js b/src/pages/TeachersUnite/ImTeacherUpdateEmailPage.js
index 5d7c1d960e3a..38065ac8ab8e 100644
--- a/src/pages/TeachersUnite/ImTeacherUpdateEmailPage.js
+++ b/src/pages/TeachersUnite/ImTeacherUpdateEmailPage.js
@@ -17,6 +17,7 @@ const defaultProps = {};
function ImTeacherUpdateEmailPage() {
const {translate} = useLocalize();
+ const activeRoute = Navigation.getActiveRouteWithoutParams();
return (
@@ -31,7 +32,7 @@ function ImTeacherUpdateEmailPage() {
title={translate('teachersUnitePage.updateYourEmail')}
subtitle={translate('teachersUnitePage.schoolMailAsDefault')}
linkKey="teachersUnitePage.contactMethods"
- onLinkPress={() => Navigation.navigate(ROUTES.SETTINGS_CONTACT_METHODS)}
+ onLinkPress={() => Navigation.navigate(ROUTES.SETTINGS_CONTACT_METHODS.getRoute(activeRoute))}
iconWidth={variables.signInLogoWidthLargeScreen}
iconHeight={variables.lhnLogoWidth}
/>
@@ -40,7 +41,7 @@ function ImTeacherUpdateEmailPage() {
success
accessibilityLabel={translate('teachersUnitePage.updateEmail')}
text={translate('teachersUnitePage.updateEmail')}
- onPress={() => Navigation.navigate(ROUTES.SETTINGS_CONTACT_METHODS)}
+ onPress={() => Navigation.navigate(ROUTES.SETTINGS_CONTACT_METHODS.getRoute(activeRoute))}
/>
diff --git a/src/pages/iou/steps/NewRequestAmountPage.js b/src/pages/iou/steps/NewRequestAmountPage.js
index e531e6706f55..a045fc6399e9 100644
--- a/src/pages/iou/steps/NewRequestAmountPage.js
+++ b/src/pages/iou/steps/NewRequestAmountPage.js
@@ -123,7 +123,7 @@ function NewRequestAmountPage({route, iou, report, selectedTab}) {
}
// Remove query from the route and encode it.
- const activeRoute = encodeURIComponent(Navigation.getActiveRoute().replace(/\?.*/, ''));
+ const activeRoute = encodeURIComponent(Navigation.getActiveRouteWithoutParams());
Navigation.navigate(ROUTES.MONEY_REQUEST_CURRENCY.getRoute(iouType, reportID, currency, activeRoute));
};
diff --git a/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.js b/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.js
index c48191957999..b97bc2521e55 100644
--- a/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.js
+++ b/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.js
@@ -123,7 +123,7 @@ class ContactMethodDetailsPage extends Component {
// Navigate to methods page on successful magic code verification
// validatedDate property is responsible to decide the status of the magic code verification
if (!prevValidatedDate && validatedDate) {
- Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS);
+ Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS.route);
}
}
@@ -236,8 +236,8 @@ class ContactMethodDetailsPage extends Component {
Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS)}
- onLinkPress={() => Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS)}
+ onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS.route)}
+ onLinkPress={() => Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS.route)}
/>
);
@@ -255,7 +255,7 @@ class ContactMethodDetailsPage extends Component {
>
Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS)}
+ onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS.route)}
/>
Navigation.goBack(ROUTES.SETTINGS_PROFILE)}
+ onBackButtonPress={() => Navigation.goBack(navigateBackTo)}
/>
diff --git a/src/pages/settings/Profile/Contacts/NewContactMethodPage.js b/src/pages/settings/Profile/Contacts/NewContactMethodPage.js
index c72f562e8c0a..ae301a9f3c33 100644
--- a/src/pages/settings/Profile/Contacts/NewContactMethodPage.js
+++ b/src/pages/settings/Profile/Contacts/NewContactMethodPage.js
@@ -103,7 +103,7 @@ function NewContactMethodPage(props) {
>
Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS)}
+ onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS.route)}
/>
policy.outputCurrency === CONST.CURRENCY.USD
- ? singleExecution(waitForNavigate(() => ReimbursementAccount.navigateToBankAccountRoute(policy.id, Navigation.getActiveRoute().replace(/\?.*/, ''))))()
+ ? singleExecution(waitForNavigate(() => ReimbursementAccount.navigateToBankAccountRoute(policy.id, Navigation.getActiveRouteWithoutParams())))()
: setIsCurrencyModalOpen(true),
brickRoadIndicator: !_.isEmpty(props.reimbursementAccount.errors) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : '',
},
diff --git a/src/styles/StyleUtils.ts b/src/styles/StyleUtils.ts
index 395013b398c9..74ea0ed06c02 100644
--- a/src/styles/StyleUtils.ts
+++ b/src/styles/StyleUtils.ts
@@ -1,5 +1,5 @@
import {CSSProperties} from 'react';
-import {Animated, DimensionValue, ImageStyle, PressableStateCallbackType, TextStyle, ViewStyle} from 'react-native';
+import {Animated, DimensionValue, ImageStyle, PressableStateCallbackType, StyleProp, TextStyle, ViewStyle} from 'react-native';
import {EdgeInsets} from 'react-native-safe-area-context';
import {ValueOf} from 'type-fest';
import * as Browser from '@libs/Browser';
@@ -16,7 +16,7 @@ import spacing from './utilities/spacing';
import variables from './variables';
type AllStyles = ViewStyle | TextStyle | ImageStyle;
-type ParsableStyle = AllStyles | ((state: PressableStateCallbackType) => AllStyles);
+type ParsableStyle = StyleProp | ((state: PressableStateCallbackType) => StyleProp);
type ColorValue = ValueOf;
type AvatarSizeName = ValueOf;
@@ -749,9 +749,8 @@ function parseStyleAsArray(styleParam: T | T[]): T[] {
/**
* Parse style function and return Styles object
*/
-function parseStyleFromFunction(style: ParsableStyle, state: PressableStateCallbackType): AllStyles[] {
- const functionAppliedStyle = typeof style === 'function' ? style(state) : style;
- return parseStyleAsArray(functionAppliedStyle);
+function parseStyleFromFunction(style: ParsableStyle, state: PressableStateCallbackType): StyleProp {
+ return typeof style === 'function' ? style(state) : style;
}
/**
diff --git a/src/types/modules/react-native.d.ts b/src/types/modules/react-native.d.ts
index a816fc77625b..ec857af2eceb 100644
--- a/src/types/modules/react-native.d.ts
+++ b/src/types/modules/react-native.d.ts
@@ -35,7 +35,7 @@ declare module 'react-native' {
'aria-haspopup'?: 'dialog' | 'grid' | 'listbox' | 'menu' | 'tree' | false;
'aria-hidden'?: boolean;
'aria-invalid'?: boolean;
- 'aria-keyshortcuts'?: string[];
+ 'aria-keyshortcuts'?: string;
'aria-label'?: string;
'aria-labelledby'?: idRef;
'aria-level'?: number;
@@ -85,7 +85,7 @@ declare module 'react-native' {
accessibilityInvalid?: boolean;
accessibilityKeyShortcuts?: string[];
accessibilityLabel?: string;
- accessibilityLabelledBy?: idRefList;
+ accessibilityLabelledBy?: idRef;
accessibilityLevel?: number;
accessibilityLiveRegion?: 'assertive' | 'none' | 'polite';
accessibilityModal?: boolean;
@@ -312,7 +312,10 @@ declare module 'react-native' {
readonly hovered: boolean;
readonly pressed: boolean;
}
- interface PressableStateCallbackType extends WebPressableStateCallbackType {}
+ interface PressableStateCallbackType extends WebPressableStateCallbackType {
+ readonly isScreenReaderActive: boolean;
+ readonly isDisabled: boolean;
+ }
// Extracted from react-native-web, packages/react-native-web/src/exports/Pressable/index.js
interface WebPressableProps extends WebSharedProps {
diff --git a/src/types/onyx/ReportAction.ts b/src/types/onyx/ReportAction.ts
index 19908273ad3d..66622f4b29ea 100644
--- a/src/types/onyx/ReportAction.ts
+++ b/src/types/onyx/ReportAction.ts
@@ -43,6 +43,9 @@ type Message = {
moderationDecision?: Decision;
translationKey?: string;
+
+ /** ID of a task report */
+ taskReportID?: string;
};
type Person = {