From f67e7532a59c496eeeba6708070c43033aae2e71 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Mon, 13 Nov 2023 09:11:22 +0100 Subject: [PATCH 001/352] migrate resizeModes to TypeScript --- src/components/Image/{resizeModes.js => resizeModes.ts} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/components/Image/{resizeModes.js => resizeModes.ts} (92%) diff --git a/src/components/Image/resizeModes.js b/src/components/Image/resizeModes.ts similarity index 92% rename from src/components/Image/resizeModes.js rename to src/components/Image/resizeModes.ts index e6cc699a2fe3..246793a9e3a3 100644 --- a/src/components/Image/resizeModes.js +++ b/src/components/Image/resizeModes.ts @@ -3,6 +3,6 @@ const RESIZE_MODES = { cover: 'cover', stretch: 'stretch', center: 'center', -}; +} as const; export default RESIZE_MODES; From ba4eda894774c0c2c37829623b55e49fee35006e Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Mon, 13 Nov 2023 09:11:42 +0100 Subject: [PATCH 002/352] start migrating Image to TypeScript --- src/components/Image/{index.js => index.tsx} | 6 ++- src/components/Image/types.ts | 41 ++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) rename src/components/Image/{index.js => index.tsx} (92%) create mode 100644 src/components/Image/types.ts diff --git a/src/components/Image/index.js b/src/components/Image/index.tsx similarity index 92% rename from src/components/Image/index.js rename to src/components/Image/index.tsx index ef1a69e19c12..68f289b3bef0 100644 --- a/src/components/Image/index.js +++ b/src/components/Image/index.tsx @@ -6,8 +6,10 @@ import _ from 'underscore'; import ONYXKEYS from '@src/ONYXKEYS'; import {defaultProps, imagePropTypes} from './imagePropTypes'; import RESIZE_MODES from './resizeModes'; +import ImageProps from './types'; -function Image(props) { +function Image(props: ImageProps) { + console.log('*** I RENDER ***', props); const {source: propsSource, isAuthTokenRequired, onLoad, session} = props; /** * Check if the image source is a URL - if so the `encryptedAuthToken` is appended @@ -19,7 +21,7 @@ function Image(props) { // in the headers of the image request so the authToken is added as a query param. // On native the authToken IS passed in the image request headers const authToken = lodashGet(session, 'encryptedAuthToken', null); - return {uri: `${propsSource.uri}?encryptedAuthToken=${encodeURIComponent(authToken)}`}; + return {uri: `${propsSource?.uri}?encryptedAuthToken=${encodeURIComponent(authToken)}`}; } return propsSource; // The session prop is not required, as it causes the image to reload whenever the session changes. For more information, please refer to issue #26034. diff --git a/src/components/Image/types.ts b/src/components/Image/types.ts new file mode 100644 index 000000000000..99ebd0bc0e8a --- /dev/null +++ b/src/components/Image/types.ts @@ -0,0 +1,41 @@ +import {ImageResizeMode, ImageSourcePropType, ImageStyle, StyleProp} from 'react-native'; +import {OnLoadEvent} from 'react-native-fast-image'; + +type ImageProps = { + /** Styles for the Image */ + style?: StyleProp; + + /** The static asset or URI source of the image */ + source: ImageSourcePropType; + + /** Should an auth token be included in the image request */ + isAuthTokenRequired: boolean; + + /** How should the image fit within its container */ + resizeMode: ImageResizeMode; + + /** Event for when the image begins loading */ + onLoadStart: () => void; + + /** Event for when the image finishes loading */ + onLoadEnd: () => void; + + /** Event for when the image is fully loaded and returns the natural dimensions of the image */ + onLoad: (event: OnLoadEvent) => void; + + /** Progress events while the image is downloading */ + onProgress: () => void; + + /* Onyx Props */ + /** Session info for the currently logged in user. */ + session: { + /** Currently logged in user authToken */ + authToken?: string; + accountId?: number; + email?: string; + encryptedAuthToken?: string; + loading: boolean; + }; +}; + +export default ImageProps; From 98bd9e171bbf0e0c6c3791bff285c5c36c254c03 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Mon, 13 Nov 2023 14:00:40 +0100 Subject: [PATCH 003/352] migrate index.js to TypeScript --- src/components/Image/index.tsx | 35 ++++++++++++++-------------------- src/components/Image/types.ts | 27 +++++++++++++------------- 2 files changed, 27 insertions(+), 35 deletions(-) diff --git a/src/components/Image/index.tsx b/src/components/Image/index.tsx index 68f289b3bef0..d161d89dcbdb 100644 --- a/src/components/Image/index.tsx +++ b/src/components/Image/index.tsx @@ -1,26 +1,23 @@ -import lodashGet from 'lodash/get'; import React, {useEffect, useMemo} from 'react'; import {Image as RNImage} from 'react-native'; import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; import ONYXKEYS from '@src/ONYXKEYS'; -import {defaultProps, imagePropTypes} from './imagePropTypes'; import RESIZE_MODES from './resizeModes'; -import ImageProps from './types'; +import {ImageOnyxProps, ImageProps, ImagePropsWithOnyx} from './types'; -function Image(props: ImageProps) { - console.log('*** I RENDER ***', props); - const {source: propsSource, isAuthTokenRequired, onLoad, session} = props; +function Image(props: ImagePropsWithOnyx) { + const {source: propsSource, isAuthTokenRequired, onLoad, session, ...forwardedProps} = props; /** * Check if the image source is a URL - if so the `encryptedAuthToken` is appended * to the source. */ const source = useMemo(() => { - if (isAuthTokenRequired) { - // There is currently a `react-native-web` bug preventing the authToken being passed - // in the headers of the image request so the authToken is added as a query param. - // On native the authToken IS passed in the image request headers - const authToken = lodashGet(session, 'encryptedAuthToken', null); + // There is currently a `react-native-web` bug preventing the authToken being passed + // in the headers of the image request so the authToken is added as a query param. + // On native the authToken IS passed in the image request headers + const authToken = session?.encryptedAuthToken ?? null; + + if (isAuthTokenRequired && authToken && typeof propsSource !== 'number') { return {uri: `${propsSource?.uri}?encryptedAuthToken=${encodeURIComponent(authToken)}`}; } return propsSource; @@ -35,7 +32,7 @@ function Image(props: ImageProps) { useEffect(() => { // If an onLoad callback was specified then manually call it and pass // the natural image dimensions to match the native API - if (onLoad == null) { + if (onLoad == null || typeof source === 'number' || !source.uri) { return; } RNImage.getSize(source.uri, (width, height) => { @@ -43,9 +40,6 @@ function Image(props: ImageProps) { }); }, [onLoad, source]); - // Omit the props which the underlying RNImage won't use - const forwardedProps = _.omit(props, ['source', 'onLoad', 'session', 'isAuthTokenRequired']); - return ( ({ session: { key: ONYXKEYS.SESSION, }, })(Image), imagePropsAreEqual, ); -ImageWithOnyx.resizeMode = RESIZE_MODES; export default ImageWithOnyx; diff --git a/src/components/Image/types.ts b/src/components/Image/types.ts index 99ebd0bc0e8a..e89fc81125cf 100644 --- a/src/components/Image/types.ts +++ b/src/components/Image/types.ts @@ -1,12 +1,20 @@ -import {ImageResizeMode, ImageSourcePropType, ImageStyle, StyleProp} from 'react-native'; +import {ImageRequireSource, ImageResizeMode, ImageStyle, ImageURISource, StyleProp} from 'react-native'; import {OnLoadEvent} from 'react-native-fast-image'; +import {OnyxEntry} from 'react-native-onyx'; +import {Session} from '@src/types/onyx'; + +type ImageOnyxProps = { + /* Onyx Props */ + /** Session info for the currently logged in user. */ + session: OnyxEntry; +}; type ImageProps = { /** Styles for the Image */ style?: StyleProp; /** The static asset or URI source of the image */ - source: ImageSourcePropType; + source: ImageURISource | ImageRequireSource; /** Should an auth token be included in the image request */ isAuthTokenRequired: boolean; @@ -25,17 +33,8 @@ type ImageProps = { /** Progress events while the image is downloading */ onProgress: () => void; - - /* Onyx Props */ - /** Session info for the currently logged in user. */ - session: { - /** Currently logged in user authToken */ - authToken?: string; - accountId?: number; - email?: string; - encryptedAuthToken?: string; - loading: boolean; - }; }; -export default ImageProps; +type ImagePropsWithOnyx = ImageOnyxProps & ImageProps; + +export type {ImageProps, ImageOnyxProps, ImagePropsWithOnyx}; From 1e9542124ffe74af8e8d1000779f0e002267fa2c Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Tue, 14 Nov 2023 09:14:32 +0100 Subject: [PATCH 004/352] migrate index.native.js to TypeScript --- .../{index.native.js => index.native.tsx} | 34 ++++++++++--------- src/components/Image/types.ts | 8 ++--- 2 files changed, 22 insertions(+), 20 deletions(-) rename src/components/Image/{index.native.js => index.native.tsx} (55%) diff --git a/src/components/Image/index.native.js b/src/components/Image/index.native.tsx similarity index 55% rename from src/components/Image/index.native.js rename to src/components/Image/index.native.tsx index cf5320392d1b..0acf57d099a9 100644 --- a/src/components/Image/index.native.js +++ b/src/components/Image/index.native.tsx @@ -1,35 +1,35 @@ -import lodashGet from 'lodash/get'; import React from 'react'; -import RNFastImage from 'react-native-fast-image'; +import {ImageRequireSource, ImageURISource} from 'react-native'; +import RNFastImage, {Source} from 'react-native-fast-image'; import {withOnyx} from 'react-native-onyx'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import {defaultProps, imagePropTypes} from './imagePropTypes'; import RESIZE_MODES from './resizeModes'; +import {ImageOnyxProps, ImagePropsWithOnyx} from './types'; -const dimensionsCache = new Map(); +const dimensionsCache = new Map(); -function resolveDimensions(key) { +function resolveDimensions(key: string) { return dimensionsCache.get(key); } -function Image(props) { +function Image(props: ImagePropsWithOnyx) { // eslint-disable-next-line react/destructuring-assignment const {source, isAuthTokenRequired, session, ...rest} = props; - let imageSource = source; - if (source && source.uri && typeof source.uri === 'number') { + let imageSource: Omit | ImageRequireSource | Source = source; + if (typeof source !== 'number' && typeof source.uri === 'number') { imageSource = source.uri; } - if (typeof imageSource !== 'number' && isAuthTokenRequired) { - const authToken = lodashGet(props, 'session.encryptedAuthToken', null); + if (typeof source !== 'number' && isAuthTokenRequired) { + const authToken = props.session?.encryptedAuthToken ?? null; imageSource = { ...source, headers: authToken ? { [CONST.CHAT_ATTACHMENT_TOKEN_KEY]: authToken, } - : null, + : undefined, }; } @@ -39,6 +39,9 @@ function Image(props) { {...rest} source={imageSource} onLoad={(evt) => { + if (typeof source === 'number' || !source.uri) { + return; + } const {width, height} = evt.nativeEvent; dimensionsCache.set(source.uri, {width, height}); if (props.onLoad) { @@ -49,15 +52,14 @@ function Image(props) { ); } -Image.propTypes = imagePropTypes; -Image.defaultProps = defaultProps; Image.displayName = 'Image'; -const ImageWithOnyx = withOnyx({ +Image.resizeMode = RESIZE_MODES; +Image.resolveDimensions = resolveDimensions; + +const ImageWithOnyx = withOnyx({ session: { key: ONYXKEYS.SESSION, }, })(Image); -ImageWithOnyx.resizeMode = RESIZE_MODES; -ImageWithOnyx.resolveDimensions = resolveDimensions; export default ImageWithOnyx; diff --git a/src/components/Image/types.ts b/src/components/Image/types.ts index e89fc81125cf..d2cd31a75f0d 100644 --- a/src/components/Image/types.ts +++ b/src/components/Image/types.ts @@ -1,5 +1,5 @@ import {ImageRequireSource, ImageResizeMode, ImageStyle, ImageURISource, StyleProp} from 'react-native'; -import {OnLoadEvent} from 'react-native-fast-image'; +import {ImageStyle as FastImageStyle, OnLoadEvent, ResizeMode, Source} from 'react-native-fast-image'; import {OnyxEntry} from 'react-native-onyx'; import {Session} from '@src/types/onyx'; @@ -11,16 +11,16 @@ type ImageOnyxProps = { type ImageProps = { /** Styles for the Image */ - style?: StyleProp; + style?: StyleProp; /** The static asset or URI source of the image */ - source: ImageURISource | ImageRequireSource; + source: Omit | ImageRequireSource | Omit; /** Should an auth token be included in the image request */ isAuthTokenRequired: boolean; /** How should the image fit within its container */ - resizeMode: ImageResizeMode; + resizeMode: ImageResizeMode & ResizeMode; /** Event for when the image begins loading */ onLoadStart: () => void; From ed90b35e0d51391264838b3df499b25ddce1b3be Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Tue, 14 Nov 2023 10:36:06 +0100 Subject: [PATCH 005/352] remove unnecessary js files --- src/components/Image/imagePropTypes.js | 51 ------------------- src/components/Image/sourcePropTypes/index.js | 11 ---- .../Image/sourcePropTypes/index.native.js | 10 ---- 3 files changed, 72 deletions(-) delete mode 100644 src/components/Image/imagePropTypes.js delete mode 100644 src/components/Image/sourcePropTypes/index.js delete mode 100644 src/components/Image/sourcePropTypes/index.native.js diff --git a/src/components/Image/imagePropTypes.js b/src/components/Image/imagePropTypes.js deleted file mode 100644 index 78bd48ba47ec..000000000000 --- a/src/components/Image/imagePropTypes.js +++ /dev/null @@ -1,51 +0,0 @@ -import PropTypes from 'prop-types'; -import stylePropTypes from '@styles/stylePropTypes'; -import RESIZE_MODES from './resizeModes'; -import sourcePropTypes from './sourcePropTypes'; - -const imagePropTypes = { - /** Styles for the Image */ - style: stylePropTypes, - - /** The static asset or URI source of the image */ - source: sourcePropTypes.isRequired, - - /** Should an auth token be included in the image request */ - isAuthTokenRequired: PropTypes.bool, - - /** How should the image fit within its container */ - resizeMode: PropTypes.string, - - /** Event for when the image begins loading */ - onLoadStart: PropTypes.func, - - /** Event for when the image finishes loading */ - onLoadEnd: PropTypes.func, - - /** Event for when the image is fully loaded and returns the natural dimensions of the image */ - onLoad: PropTypes.func, - - /** Progress events while the image is downloading */ - onProgress: PropTypes.func, - - /* Onyx Props */ - /** Session info for the currently logged in user. */ - session: PropTypes.shape({ - /** Currently logged in user authToken */ - authToken: PropTypes.string, - }), -}; - -const defaultProps = { - style: [], - session: { - authToken: null, - }, - isAuthTokenRequired: false, - resizeMode: RESIZE_MODES.cover, - onLoadStart: () => {}, - onLoadEnd: () => {}, - onLoad: () => {}, -}; - -export {imagePropTypes, defaultProps}; diff --git a/src/components/Image/sourcePropTypes/index.js b/src/components/Image/sourcePropTypes/index.js deleted file mode 100644 index 99e88b5cf343..000000000000 --- a/src/components/Image/sourcePropTypes/index.js +++ /dev/null @@ -1,11 +0,0 @@ -import PropTypes from 'prop-types'; - -export default PropTypes.oneOfType([ - PropTypes.number, - PropTypes.shape({ - uri: PropTypes.string.isRequired, - // eslint-disable-next-line react/forbid-prop-types - headers: PropTypes.object, - }), - PropTypes.string, -]); diff --git a/src/components/Image/sourcePropTypes/index.native.js b/src/components/Image/sourcePropTypes/index.native.js deleted file mode 100644 index 629b21852613..000000000000 --- a/src/components/Image/sourcePropTypes/index.native.js +++ /dev/null @@ -1,10 +0,0 @@ -import PropTypes from 'prop-types'; - -export default PropTypes.oneOfType([ - PropTypes.number, - PropTypes.shape({ - uri: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, - // eslint-disable-next-line react/forbid-prop-types - headers: PropTypes.object, - }), -]); From c1d0c2c6885960897ed7e9c547a11262b5347271 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Tue, 14 Nov 2023 10:37:14 +0100 Subject: [PATCH 006/352] extract map type --- src/components/Image/index.native.tsx | 4 ++-- src/components/Image/types.ts | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/components/Image/index.native.tsx b/src/components/Image/index.native.tsx index 0acf57d099a9..b9c36c7d1b9e 100644 --- a/src/components/Image/index.native.tsx +++ b/src/components/Image/index.native.tsx @@ -5,9 +5,9 @@ import {withOnyx} from 'react-native-onyx'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import RESIZE_MODES from './resizeModes'; -import {ImageOnyxProps, ImagePropsWithOnyx} from './types'; +import {DimensionsCacheValue, ImageOnyxProps, ImagePropsWithOnyx} from './types'; -const dimensionsCache = new Map(); +const dimensionsCache = new Map(); function resolveDimensions(key: string) { return dimensionsCache.get(key); diff --git a/src/components/Image/types.ts b/src/components/Image/types.ts index d2cd31a75f0d..5c46e25e8d12 100644 --- a/src/components/Image/types.ts +++ b/src/components/Image/types.ts @@ -37,4 +37,9 @@ type ImageProps = { type ImagePropsWithOnyx = ImageOnyxProps & ImageProps; -export type {ImageProps, ImageOnyxProps, ImagePropsWithOnyx}; +type DimensionsCacheValue = { + width: number; + height: number; +}; + +export type {ImageProps, ImageOnyxProps, ImagePropsWithOnyx, DimensionsCacheValue}; From 46c9f9269ce4d3988677757256b2da53fc0586d6 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Tue, 14 Nov 2023 11:17:21 +0100 Subject: [PATCH 007/352] bring back optional props --- src/components/Image/types.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/components/Image/types.ts b/src/components/Image/types.ts index 5c46e25e8d12..d91345b1690d 100644 --- a/src/components/Image/types.ts +++ b/src/components/Image/types.ts @@ -17,22 +17,25 @@ type ImageProps = { source: Omit | ImageRequireSource | Omit; /** Should an auth token be included in the image request */ - isAuthTokenRequired: boolean; + isAuthTokenRequired?: boolean; /** How should the image fit within its container */ - resizeMode: ImageResizeMode & ResizeMode; + resizeMode?: ImageResizeMode & ResizeMode; /** Event for when the image begins loading */ - onLoadStart: () => void; + onLoadStart?: () => void; /** Event for when the image finishes loading */ - onLoadEnd: () => void; + onLoadEnd?: () => void; + + /** Error handler */ + onError?: () => void; /** Event for when the image is fully loaded and returns the natural dimensions of the image */ - onLoad: (event: OnLoadEvent) => void; + onLoad?: (event: OnLoadEvent) => void; /** Progress events while the image is downloading */ - onProgress: () => void; + onProgress?: () => void; }; type ImagePropsWithOnyx = ImageOnyxProps & ImageProps; From 0041ba83f1011b334256e2313b9d6e75205ff1eb Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Tue, 14 Nov 2023 12:16:36 +0100 Subject: [PATCH 008/352] destructure props, add return types --- src/components/Image/index.native.tsx | 13 +++++-------- src/components/Image/index.tsx | 3 +-- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/components/Image/index.native.tsx b/src/components/Image/index.native.tsx index b9c36c7d1b9e..2196eaa17631 100644 --- a/src/components/Image/index.native.tsx +++ b/src/components/Image/index.native.tsx @@ -9,20 +9,17 @@ import {DimensionsCacheValue, ImageOnyxProps, ImagePropsWithOnyx} from './types' const dimensionsCache = new Map(); -function resolveDimensions(key: string) { +function resolveDimensions(key: string): DimensionsCacheValue | undefined { return dimensionsCache.get(key); } -function Image(props: ImagePropsWithOnyx) { - // eslint-disable-next-line react/destructuring-assignment - const {source, isAuthTokenRequired, session, ...rest} = props; - +function Image({source, isAuthTokenRequired, session, ...rest}: ImagePropsWithOnyx) { let imageSource: Omit | ImageRequireSource | Source = source; if (typeof source !== 'number' && typeof source.uri === 'number') { imageSource = source.uri; } if (typeof source !== 'number' && isAuthTokenRequired) { - const authToken = props.session?.encryptedAuthToken ?? null; + const authToken = session?.encryptedAuthToken ?? null; imageSource = { ...source, headers: authToken @@ -44,8 +41,8 @@ function Image(props: ImagePropsWithOnyx) { } const {width, height} = evt.nativeEvent; dimensionsCache.set(source.uri, {width, height}); - if (props.onLoad) { - props.onLoad(evt); + if (rest.onLoad) { + rest.onLoad(evt); } }} /> diff --git a/src/components/Image/index.tsx b/src/components/Image/index.tsx index d161d89dcbdb..9da6020f7fb9 100644 --- a/src/components/Image/index.tsx +++ b/src/components/Image/index.tsx @@ -5,8 +5,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import RESIZE_MODES from './resizeModes'; import {ImageOnyxProps, ImageProps, ImagePropsWithOnyx} from './types'; -function Image(props: ImagePropsWithOnyx) { - const {source: propsSource, isAuthTokenRequired, onLoad, session, ...forwardedProps} = props; +function Image({source: propsSource, isAuthTokenRequired, onLoad, session, ...forwardedProps}: ImagePropsWithOnyx) { /** * Check if the image source is a URL - if so the `encryptedAuthToken` is appended * to the source. From b44660936a731c44db6b9ac173e5169d6bef5fba Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Wed, 15 Nov 2023 08:43:43 +0100 Subject: [PATCH 009/352] extract source type, improve if statements --- src/components/Image/index.native.tsx | 7 +++---- src/components/Image/index.tsx | 6 +++--- src/components/Image/types.ts | 4 +++- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/components/Image/index.native.tsx b/src/components/Image/index.native.tsx index 2196eaa17631..70be8f9f995e 100644 --- a/src/components/Image/index.native.tsx +++ b/src/components/Image/index.native.tsx @@ -1,11 +1,10 @@ import React from 'react'; -import {ImageRequireSource, ImageURISource} from 'react-native'; -import RNFastImage, {Source} from 'react-native-fast-image'; +import RNFastImage from 'react-native-fast-image'; import {withOnyx} from 'react-native-onyx'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import RESIZE_MODES from './resizeModes'; -import {DimensionsCacheValue, ImageOnyxProps, ImagePropsWithOnyx} from './types'; +import {DimensionsCacheValue, FastImageSource, ImageOnyxProps, ImagePropsWithOnyx} from './types'; const dimensionsCache = new Map(); @@ -14,7 +13,7 @@ function resolveDimensions(key: string): DimensionsCacheValue | undefined { } function Image({source, isAuthTokenRequired, session, ...rest}: ImagePropsWithOnyx) { - let imageSource: Omit | ImageRequireSource | Source = source; + let imageSource: FastImageSource = source; if (typeof source !== 'number' && typeof source.uri === 'number') { imageSource = source.uri; } diff --git a/src/components/Image/index.tsx b/src/components/Image/index.tsx index 9da6020f7fb9..61a11e31f163 100644 --- a/src/components/Image/index.tsx +++ b/src/components/Image/index.tsx @@ -16,8 +16,8 @@ function Image({source: propsSource, isAuthTokenRequired, onLoad, session, ...fo // On native the authToken IS passed in the image request headers const authToken = session?.encryptedAuthToken ?? null; - if (isAuthTokenRequired && authToken && typeof propsSource !== 'number') { - return {uri: `${propsSource?.uri}?encryptedAuthToken=${encodeURIComponent(authToken)}`}; + if (isAuthTokenRequired && authToken && typeof propsSource !== 'number' && propsSource.uri) { + return {uri: `${propsSource.uri}?encryptedAuthToken=${encodeURIComponent(authToken)}`}; } return propsSource; // The session prop is not required, as it causes the image to reload whenever the session changes. For more information, please refer to issue #26034. @@ -31,7 +31,7 @@ function Image({source: propsSource, isAuthTokenRequired, onLoad, session, ...fo useEffect(() => { // If an onLoad callback was specified then manually call it and pass // the natural image dimensions to match the native API - if (onLoad == null || typeof source === 'number' || !source.uri) { + if (typeof onLoad !== 'function' || typeof source === 'number' || !source.uri) { return; } RNImage.getSize(source.uri, (width, height) => { diff --git a/src/components/Image/types.ts b/src/components/Image/types.ts index d91345b1690d..3edf97b15e71 100644 --- a/src/components/Image/types.ts +++ b/src/components/Image/types.ts @@ -45,4 +45,6 @@ type DimensionsCacheValue = { height: number; }; -export type {ImageProps, ImageOnyxProps, ImagePropsWithOnyx, DimensionsCacheValue}; +type FastImageSource = Omit | ImageRequireSource | Source; + +export type {ImageProps, ImageOnyxProps, ImagePropsWithOnyx, DimensionsCacheValue, FastImageSource}; From 21c79807f87386247df859426101a6ff56ef357a Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Wed, 15 Nov 2023 12:48:51 +0100 Subject: [PATCH 010/352] change Image prop types names --- src/components/Image/index.native.tsx | 6 +++--- src/components/Image/index.tsx | 8 ++++---- src/components/Image/types.ts | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/components/Image/index.native.tsx b/src/components/Image/index.native.tsx index 70be8f9f995e..d75b768b27b6 100644 --- a/src/components/Image/index.native.tsx +++ b/src/components/Image/index.native.tsx @@ -4,7 +4,7 @@ import {withOnyx} from 'react-native-onyx'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import RESIZE_MODES from './resizeModes'; -import {DimensionsCacheValue, FastImageSource, ImageOnyxProps, ImagePropsWithOnyx} from './types'; +import {DimensionsCacheValue, FastImageSource, ImageOnyxProps, ImageProps} from './types'; const dimensionsCache = new Map(); @@ -12,7 +12,7 @@ function resolveDimensions(key: string): DimensionsCacheValue | undefined { return dimensionsCache.get(key); } -function Image({source, isAuthTokenRequired, session, ...rest}: ImagePropsWithOnyx) { +function Image({source, isAuthTokenRequired, session, ...rest}: ImageProps) { let imageSource: FastImageSource = source; if (typeof source !== 'number' && typeof source.uri === 'number') { imageSource = source.uri; @@ -52,7 +52,7 @@ Image.displayName = 'Image'; Image.resizeMode = RESIZE_MODES; Image.resolveDimensions = resolveDimensions; -const ImageWithOnyx = withOnyx({ +const ImageWithOnyx = withOnyx({ session: { key: ONYXKEYS.SESSION, }, diff --git a/src/components/Image/index.tsx b/src/components/Image/index.tsx index 61a11e31f163..ecf1d8fb504c 100644 --- a/src/components/Image/index.tsx +++ b/src/components/Image/index.tsx @@ -3,9 +3,9 @@ import {Image as RNImage} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import ONYXKEYS from '@src/ONYXKEYS'; import RESIZE_MODES from './resizeModes'; -import {ImageOnyxProps, ImageProps, ImagePropsWithOnyx} from './types'; +import {ImageOnyxProps, ImageOwnProps, ImageProps} from './types'; -function Image({source: propsSource, isAuthTokenRequired, onLoad, session, ...forwardedProps}: ImagePropsWithOnyx) { +function Image({source: propsSource, isAuthTokenRequired, onLoad, session, ...forwardedProps}: ImageProps) { /** * Check if the image source is a URL - if so the `encryptedAuthToken` is appended * to the source. @@ -48,7 +48,7 @@ function Image({source: propsSource, isAuthTokenRequired, onLoad, session, ...fo ); } -function imagePropsAreEqual(prevProps: ImageProps, nextProps: ImageProps) { +function imagePropsAreEqual(prevProps: ImageOwnProps, nextProps: ImageOwnProps) { return prevProps.source === nextProps.source; } @@ -56,7 +56,7 @@ Image.resizeMode = RESIZE_MODES; Image.displayName = 'Image'; const ImageWithOnyx = React.memo( - withOnyx({ + withOnyx({ session: { key: ONYXKEYS.SESSION, }, diff --git a/src/components/Image/types.ts b/src/components/Image/types.ts index 3edf97b15e71..2ddf3975af8c 100644 --- a/src/components/Image/types.ts +++ b/src/components/Image/types.ts @@ -9,7 +9,7 @@ type ImageOnyxProps = { session: OnyxEntry; }; -type ImageProps = { +type ImageOwnProps = { /** Styles for the Image */ style?: StyleProp; @@ -38,7 +38,7 @@ type ImageProps = { onProgress?: () => void; }; -type ImagePropsWithOnyx = ImageOnyxProps & ImageProps; +type ImageProps = ImageOnyxProps & ImageOwnProps; type DimensionsCacheValue = { width: number; @@ -47,4 +47,4 @@ type DimensionsCacheValue = { type FastImageSource = Omit | ImageRequireSource | Source; -export type {ImageProps, ImageOnyxProps, ImagePropsWithOnyx, DimensionsCacheValue, FastImageSource}; +export type {ImageOwnProps, ImageOnyxProps, ImageProps, DimensionsCacheValue, FastImageSource}; From f11591421b72221fa191c0b6311898d417161751 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Wed, 15 Nov 2023 13:41:31 +0100 Subject: [PATCH 011/352] bring back imageSource to if statement --- src/components/Image/index.native.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Image/index.native.tsx b/src/components/Image/index.native.tsx index d75b768b27b6..981b8c1eaf2e 100644 --- a/src/components/Image/index.native.tsx +++ b/src/components/Image/index.native.tsx @@ -17,7 +17,7 @@ function Image({source, isAuthTokenRequired, session, ...rest}: ImageProps) { if (typeof source !== 'number' && typeof source.uri === 'number') { imageSource = source.uri; } - if (typeof source !== 'number' && isAuthTokenRequired) { + if (typeof imageSource !== 'number' && typeof source !== 'number' && isAuthTokenRequired) { const authToken = session?.encryptedAuthToken ?? null; imageSource = { ...source, From e2508f9d16c1a8e0627d8a4cf514d53fffcb59a2 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Fri, 17 Nov 2023 08:24:17 +0100 Subject: [PATCH 012/352] remove resolveDimensions from native Image --- src/components/Image/index.native.tsx | 18 ++---------------- src/components/Image/types.ts | 7 +------ 2 files changed, 3 insertions(+), 22 deletions(-) diff --git a/src/components/Image/index.native.tsx b/src/components/Image/index.native.tsx index 981b8c1eaf2e..02127c4ae63d 100644 --- a/src/components/Image/index.native.tsx +++ b/src/components/Image/index.native.tsx @@ -4,13 +4,7 @@ import {withOnyx} from 'react-native-onyx'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import RESIZE_MODES from './resizeModes'; -import {DimensionsCacheValue, FastImageSource, ImageOnyxProps, ImageProps} from './types'; - -const dimensionsCache = new Map(); - -function resolveDimensions(key: string): DimensionsCacheValue | undefined { - return dimensionsCache.get(key); -} +import {FastImageSource, ImageOnyxProps, ImageProps} from './types'; function Image({source, isAuthTokenRequired, session, ...rest}: ImageProps) { let imageSource: FastImageSource = source; @@ -35,14 +29,7 @@ function Image({source, isAuthTokenRequired, session, ...rest}: ImageProps) { {...rest} source={imageSource} onLoad={(evt) => { - if (typeof source === 'number' || !source.uri) { - return; - } - const {width, height} = evt.nativeEvent; - dimensionsCache.set(source.uri, {width, height}); - if (rest.onLoad) { - rest.onLoad(evt); - } + rest.onLoad?.(evt); }} /> ); @@ -50,7 +37,6 @@ function Image({source, isAuthTokenRequired, session, ...rest}: ImageProps) { Image.displayName = 'Image'; Image.resizeMode = RESIZE_MODES; -Image.resolveDimensions = resolveDimensions; const ImageWithOnyx = withOnyx({ session: { diff --git a/src/components/Image/types.ts b/src/components/Image/types.ts index 2ddf3975af8c..60479e9dee50 100644 --- a/src/components/Image/types.ts +++ b/src/components/Image/types.ts @@ -40,11 +40,6 @@ type ImageOwnProps = { type ImageProps = ImageOnyxProps & ImageOwnProps; -type DimensionsCacheValue = { - width: number; - height: number; -}; - type FastImageSource = Omit | ImageRequireSource | Source; -export type {ImageOwnProps, ImageOnyxProps, ImageProps, DimensionsCacheValue, FastImageSource}; +export type {ImageOwnProps, ImageOnyxProps, ImageProps, FastImageSource}; From 3f260bc6d9407a3ecbc7b661f419e65a17280698 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Fri, 17 Nov 2023 08:40:04 +0100 Subject: [PATCH 013/352] remove references to Image.resizeModes --- src/components/ImageView/index.js | 3 ++- src/components/ImageView/index.native.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/ImageView/index.js b/src/components/ImageView/index.js index a733466e1ae2..8da3f9ca4770 100644 --- a/src/components/ImageView/index.js +++ b/src/components/ImageView/index.js @@ -3,6 +3,7 @@ import React, {useCallback, useEffect, useRef, useState} from 'react'; import {View} from 'react-native'; import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import Image from '@components/Image'; +import RESIZE_MODES from '@components/Image/resizeModes'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import styles from '@styles/styles'; @@ -238,7 +239,7 @@ function ImageView({isAuthTokenRequired, url, fileName, onError}) { style={isLoading || zoomScale === 0 ? undefined : [styles.w100, styles.h100]} // When Image dimensions are lower than the container boundary(zoomscale <= 1), use `contain` to render the image with natural dimensions. // Both `center` and `contain` keeps the image centered on both x and y axis. - resizeMode={zoomScale > 1 ? Image.resizeMode.center : Image.resizeMode.contain} + resizeMode={zoomScale > 1 ? RESIZE_MODES.center : RESIZE_MODES.contain} onLoadStart={imageLoadingStart} onLoad={imageLoad} onError={onError} diff --git a/src/components/ImageView/index.native.js b/src/components/ImageView/index.native.js index dd17e2d27a4e..d8124f30cde9 100644 --- a/src/components/ImageView/index.native.js +++ b/src/components/ImageView/index.native.js @@ -5,6 +5,7 @@ import ImageZoom from 'react-native-image-pan-zoom'; import _ from 'underscore'; import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import Image from '@components/Image'; +import RESIZE_MODES from '@components/Image/resizeModes'; import useWindowDimensions from '@hooks/useWindowDimensions'; import styles from '@styles/styles'; import variables from '@styles/variables'; @@ -218,7 +219,7 @@ function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { ]} source={{uri: url}} isAuthTokenRequired={isAuthTokenRequired} - resizeMode={Image.resizeMode.contain} + resizeMode={RESIZE_MODES.contain} onLoadStart={imageLoadingStart} onLoad={configureImageZoom} /> From 3a76fb01e498973e4ef358670c60b7efcc181637 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Mon, 20 Nov 2023 12:36:51 +0100 Subject: [PATCH 014/352] remove RESIZE_MODES from Image --- src/components/Image/index.native.tsx | 6 ++---- src/components/Image/index.tsx | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/components/Image/index.native.tsx b/src/components/Image/index.native.tsx index 02127c4ae63d..1d2d33501644 100644 --- a/src/components/Image/index.native.tsx +++ b/src/components/Image/index.native.tsx @@ -3,7 +3,6 @@ import RNFastImage from 'react-native-fast-image'; import {withOnyx} from 'react-native-onyx'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import RESIZE_MODES from './resizeModes'; import {FastImageSource, ImageOnyxProps, ImageProps} from './types'; function Image({source, isAuthTokenRequired, session, ...rest}: ImageProps) { @@ -35,13 +34,12 @@ function Image({source, isAuthTokenRequired, session, ...rest}: ImageProps) { ); } -Image.displayName = 'Image'; -Image.resizeMode = RESIZE_MODES; - const ImageWithOnyx = withOnyx({ session: { key: ONYXKEYS.SESSION, }, })(Image); +ImageWithOnyx.displayName = 'Image'; + export default ImageWithOnyx; diff --git a/src/components/Image/index.tsx b/src/components/Image/index.tsx index ecf1d8fb504c..0d867c831919 100644 --- a/src/components/Image/index.tsx +++ b/src/components/Image/index.tsx @@ -2,7 +2,6 @@ import React, {useEffect, useMemo} from 'react'; import {Image as RNImage} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import ONYXKEYS from '@src/ONYXKEYS'; -import RESIZE_MODES from './resizeModes'; import {ImageOnyxProps, ImageOwnProps, ImageProps} from './types'; function Image({source: propsSource, isAuthTokenRequired, onLoad, session, ...forwardedProps}: ImageProps) { @@ -52,9 +51,6 @@ function imagePropsAreEqual(prevProps: ImageOwnProps, nextProps: ImageOwnProps) return prevProps.source === nextProps.source; } -Image.resizeMode = RESIZE_MODES; -Image.displayName = 'Image'; - const ImageWithOnyx = React.memo( withOnyx({ session: { @@ -64,4 +60,6 @@ const ImageWithOnyx = React.memo( imagePropsAreEqual, ); +ImageWithOnyx.displayName = 'Image'; + export default ImageWithOnyx; From 02daaf589d85faac1b8a7a67fdd983f7c78a5405 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Mon, 27 Nov 2023 11:26:57 +0100 Subject: [PATCH 015/352] remove unnecessary comment --- src/components/Image/types.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Image/types.ts b/src/components/Image/types.ts index 60479e9dee50..9f49ea16f076 100644 --- a/src/components/Image/types.ts +++ b/src/components/Image/types.ts @@ -4,7 +4,6 @@ import {OnyxEntry} from 'react-native-onyx'; import {Session} from '@src/types/onyx'; type ImageOnyxProps = { - /* Onyx Props */ /** Session info for the currently logged in user. */ session: OnyxEntry; }; From 0e11fb813a21955b81a8aa0f21bce1abed463834 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Mon, 27 Nov 2023 11:41:23 +0100 Subject: [PATCH 016/352] remove Image.resizeMode --- src/components/ImageView/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ImageView/index.js b/src/components/ImageView/index.js index e2a76bf2a1f8..b96222a17c7c 100644 --- a/src/components/ImageView/index.js +++ b/src/components/ImageView/index.js @@ -271,7 +271,7 @@ function ImageView({isAuthTokenRequired, url, fileName, onError}) { source={{uri: url}} isAuthTokenRequired={isAuthTokenRequired} style={[styles.h100, styles.w100]} - resizeMode={Image.resizeMode.contain} + resizeMode={RESIZE_MODES.contain} onLoadStart={imageLoadingStart} onLoad={imageLoad} onError={onError} From 06c374c064ad50e7d75918b3c0972b9279b20da9 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Mon, 15 Jan 2024 15:49:33 +0100 Subject: [PATCH 017/352] adjust types to expo-image --- src/components/Image/index.native.tsx | 4 ++-- src/components/Image/index.tsx | 7 ++++--- src/components/Image/types.ts | 10 +++++----- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/components/Image/index.native.tsx b/src/components/Image/index.native.tsx index 50451c20d5d3..27f00adbb2ce 100644 --- a/src/components/Image/index.native.tsx +++ b/src/components/Image/index.native.tsx @@ -3,7 +3,7 @@ import React from 'react'; import {withOnyx} from 'react-native-onyx'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {ImageOnyxProps, ImageProps, ImageSource} from './types'; +import type {ExpoImageSource, ImageOnyxProps, ImageProps} from './types'; const dimensionsCache = new Map(); @@ -11,7 +11,7 @@ function Image(props: ImageProps) { // eslint-disable-next-line react/destructuring-assignment const {source, isAuthTokenRequired, session, ...rest} = props; - let imageSource: ImageSource = source; + let imageSource: ExpoImageSource = source; if (source && typeof source === 'object' && 'uri' in source && typeof source.uri === 'number') { imageSource = source.uri; } diff --git a/src/components/Image/index.tsx b/src/components/Image/index.tsx index 0d867c831919..930cb89dee74 100644 --- a/src/components/Image/index.tsx +++ b/src/components/Image/index.tsx @@ -2,7 +2,7 @@ import React, {useEffect, useMemo} from 'react'; import {Image as RNImage} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import ONYXKEYS from '@src/ONYXKEYS'; -import {ImageOnyxProps, ImageOwnProps, ImageProps} from './types'; +import type {ImageOnyxProps, ImageOwnProps, ImageProps} from './types'; function Image({source: propsSource, isAuthTokenRequired, onLoad, session, ...forwardedProps}: ImageProps) { /** @@ -15,7 +15,7 @@ function Image({source: propsSource, isAuthTokenRequired, onLoad, session, ...fo // On native the authToken IS passed in the image request headers const authToken = session?.encryptedAuthToken ?? null; - if (isAuthTokenRequired && authToken && typeof propsSource !== 'number' && propsSource.uri) { + if (isAuthTokenRequired && authToken && typeof propsSource === 'object' && propsSource && 'uri' in propsSource) { return {uri: `${propsSource.uri}?encryptedAuthToken=${encodeURIComponent(authToken)}`}; } return propsSource; @@ -30,9 +30,10 @@ function Image({source: propsSource, isAuthTokenRequired, onLoad, session, ...fo useEffect(() => { // If an onLoad callback was specified then manually call it and pass // the natural image dimensions to match the native API - if (typeof onLoad !== 'function' || typeof source === 'number' || !source.uri) { + if (typeof onLoad !== 'function' || !source || typeof source !== 'object' || !('uri' in source) || !source?.uri) { return; } + RNImage.getSize(source.uri, (width, height) => { onLoad({nativeEvent: {width, height}}); }); diff --git a/src/components/Image/types.ts b/src/components/Image/types.ts index e792a0c22c6f..e194a545aec4 100644 --- a/src/components/Image/types.ts +++ b/src/components/Image/types.ts @@ -1,9 +1,9 @@ -import type {ImageSource as ExpoImageSource} from 'expo-image'; -import type {ImageResizeMode, ImageStyle, StyleProp} from 'react-native'; +import type {ImageSource} from 'expo-image'; +import type {ImageRequireSource, ImageResizeMode, ImageStyle, ImageURISource, StyleProp} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import type {Session} from '@src/types/onyx'; -type ImageSource = ExpoImageSource | string | number | ExpoImageSource[] | string[] | null; +type ExpoImageSource = ImageSource | string | number | ImageSource[] | string[] | null; type ImageOnyxProps = { /** Session info for the currently logged in user. */ @@ -15,7 +15,7 @@ type ImageOwnProps = { style?: StyleProp; /** The static asset or URI source of the image */ - source: ImageSource; + source: ExpoImageSource | Omit | ImageRequireSource; /** Should an auth token be included in the image request */ isAuthTokenRequired?: boolean; @@ -46,4 +46,4 @@ type ImageOwnProps = { type ImageProps = ImageOnyxProps & ImageOwnProps; -export type {ImageOwnProps, ImageOnyxProps, ImageProps, ImageSource}; +export type {ImageOwnProps, ImageOnyxProps, ImageProps, ExpoImageSource}; From e9451f09734ab8bbea510e36e6104db64b33c064 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Tue, 16 Jan 2024 15:53:12 +0100 Subject: [PATCH 018/352] fix source type --- src/components/Image/types.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/Image/types.ts b/src/components/Image/types.ts index e194a545aec4..ba351b8031af 100644 --- a/src/components/Image/types.ts +++ b/src/components/Image/types.ts @@ -3,7 +3,8 @@ import type {ImageRequireSource, ImageResizeMode, ImageStyle, ImageURISource, St import type {OnyxEntry} from 'react-native-onyx'; import type {Session} from '@src/types/onyx'; -type ExpoImageSource = ImageSource | string | number | ImageSource[] | string[] | null; +// type ExpoImageSource = ImageSource | string | number | ImageSource[] | string[] | null; +type ExpoImageSource = ImageSource | number | ImageSource[]; type ImageOnyxProps = { /** Session info for the currently logged in user. */ From ee813605c7b3516633c146118e7b891d5892dd41 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Thu, 18 Jan 2024 15:14:36 +0100 Subject: [PATCH 019/352] bring back sourcePropTypes directory --- src/components/Avatar.tsx | 4 ++-- src/components/Image/sourcePropTypes/index.js | 13 +++++++++++++ .../Image/sourcePropTypes/index.native.js | 12 ++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 src/components/Image/sourcePropTypes/index.js create mode 100644 src/components/Image/sourcePropTypes/index.native.js diff --git a/src/components/Avatar.tsx b/src/components/Avatar.tsx index 4da91c2e7d19..6b4e2cc2eb4d 100644 --- a/src/components/Avatar.tsx +++ b/src/components/Avatar.tsx @@ -1,5 +1,5 @@ import React, {useEffect, useState} from 'react'; -import type {StyleProp, ViewStyle} from 'react-native'; +import type {ImageStyle, StyleProp, ViewStyle} from 'react-native'; import {View} from 'react-native'; import useNetwork from '@hooks/useNetwork'; import useStyleUtils from '@hooks/useStyleUtils'; @@ -112,7 +112,7 @@ function Avatar({ } onError={() => setImageError(true)} /> diff --git a/src/components/Image/sourcePropTypes/index.js b/src/components/Image/sourcePropTypes/index.js new file mode 100644 index 000000000000..c5102b240cb1 --- /dev/null +++ b/src/components/Image/sourcePropTypes/index.js @@ -0,0 +1,13 @@ +import PropTypes from 'prop-types'; + +export default PropTypes.oneOfType([ + PropTypes.func, + PropTypes.elementType, + PropTypes.number, + PropTypes.shape({ + uri: PropTypes.string, + // eslint-disable-next-line react/forbid-prop-types + headers: PropTypes.object, + }), + PropTypes.string, +]); diff --git a/src/components/Image/sourcePropTypes/index.native.js b/src/components/Image/sourcePropTypes/index.native.js new file mode 100644 index 000000000000..ade254718879 --- /dev/null +++ b/src/components/Image/sourcePropTypes/index.native.js @@ -0,0 +1,12 @@ +import PropTypes from 'prop-types'; + +export default PropTypes.oneOfType([ + PropTypes.func, + PropTypes.elementType, + PropTypes.number, + PropTypes.shape({ + uri: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, + // eslint-disable-next-line react/forbid-prop-types + headers: PropTypes.object, + }), +]); From 9ceb2eb12f72840d86bf68c2b1f3ca5b768d0150 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Thu, 18 Jan 2024 15:55:02 +0100 Subject: [PATCH 020/352] fix type errors in Avatar.tsx --- src/components/Avatar.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/Avatar.tsx b/src/components/Avatar.tsx index 6b4e2cc2eb4d..23759dbee937 100644 --- a/src/components/Avatar.tsx +++ b/src/components/Avatar.tsx @@ -92,7 +92,15 @@ function Avatar({ return ( - {typeof avatarSource === 'function' || typeof avatarSource === 'number' ? ( + {typeof avatarSource === 'string' ? ( + + } + onError={() => setImageError(true)} + /> + + ) : ( - ) : ( - - } - onError={() => setImageError(true)} - /> - )} ); From 50726c72d5b0e09c4c5b5c692adebde3290323e3 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Thu, 18 Jan 2024 15:56:33 +0100 Subject: [PATCH 021/352] remove unnecessary comment --- src/components/Image/types.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Image/types.ts b/src/components/Image/types.ts index ba351b8031af..7e344e608c48 100644 --- a/src/components/Image/types.ts +++ b/src/components/Image/types.ts @@ -3,7 +3,6 @@ import type {ImageRequireSource, ImageResizeMode, ImageStyle, ImageURISource, St import type {OnyxEntry} from 'react-native-onyx'; import type {Session} from '@src/types/onyx'; -// type ExpoImageSource = ImageSource | string | number | ImageSource[] | string[] | null; type ExpoImageSource = ImageSource | number | ImageSource[]; type ImageOnyxProps = { From 4c69801821da6cc897f74d6b4750f9d3c6f2cef8 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Thu, 18 Jan 2024 16:11:12 +0100 Subject: [PATCH 022/352] simplify source related if statements --- src/components/Image/index.native.tsx | 3 ++- src/components/Image/index.tsx | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/Image/index.native.tsx b/src/components/Image/index.native.tsx index 27f00adbb2ce..22466cd938b9 100644 --- a/src/components/Image/index.native.tsx +++ b/src/components/Image/index.native.tsx @@ -12,7 +12,8 @@ function Image(props: ImageProps) { const {source, isAuthTokenRequired, session, ...rest} = props; let imageSource: ExpoImageSource = source; - if (source && typeof source === 'object' && 'uri' in source && typeof source.uri === 'number') { + + if (typeof source === 'object' && 'uri' in source && typeof source.uri === 'number') { imageSource = source.uri; } if (typeof imageSource !== 'number' && isAuthTokenRequired && typeof source === 'object') { diff --git a/src/components/Image/index.tsx b/src/components/Image/index.tsx index 930cb89dee74..2f9cff851438 100644 --- a/src/components/Image/index.tsx +++ b/src/components/Image/index.tsx @@ -15,7 +15,7 @@ function Image({source: propsSource, isAuthTokenRequired, onLoad, session, ...fo // On native the authToken IS passed in the image request headers const authToken = session?.encryptedAuthToken ?? null; - if (isAuthTokenRequired && authToken && typeof propsSource === 'object' && propsSource && 'uri' in propsSource) { + if (isAuthTokenRequired && authToken && typeof propsSource === 'object' && 'uri' in propsSource) { return {uri: `${propsSource.uri}?encryptedAuthToken=${encodeURIComponent(authToken)}`}; } return propsSource; @@ -30,7 +30,7 @@ function Image({source: propsSource, isAuthTokenRequired, onLoad, session, ...fo useEffect(() => { // If an onLoad callback was specified then manually call it and pass // the natural image dimensions to match the native API - if (typeof onLoad !== 'function' || !source || typeof source !== 'object' || !('uri' in source) || !source?.uri) { + if (typeof onLoad !== 'function' || typeof source !== 'object' || !('uri' in source) || !source?.uri) { return; } From 1056c9f3238b95125b27037e64c1e93e334d36c0 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Mon, 5 Feb 2024 09:59:28 +0100 Subject: [PATCH 023/352] fix headers type issue --- src/components/Image/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Image/index.tsx b/src/components/Image/index.tsx index a74ae33a1764..9ca27cae3a1d 100644 --- a/src/components/Image/index.tsx +++ b/src/components/Image/index.tsx @@ -16,7 +16,7 @@ function Image({source: propsSource, isAuthTokenRequired, session, ...forwardedP return { ...propsSource, headers: { - [CONST.CHAT_ATTACHMENT_TOKEN_KEY]: authToken, + [CONST.CHAT_ATTACHMENT_TOKEN_KEY]: authToken ?? '', }, }; } From 611227e397444a3366b93e099d9df09737e3f972 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Mon, 5 Feb 2024 15:20:52 +0100 Subject: [PATCH 024/352] migrate index.native.js to TypeScript --- .../{index.native.js => index.native.tsx} | 25 ++++++------------- 1 file changed, 8 insertions(+), 17 deletions(-) rename src/components/Image/{index.native.js => index.native.tsx} (64%) diff --git a/src/components/Image/index.native.js b/src/components/Image/index.native.tsx similarity index 64% rename from src/components/Image/index.native.js rename to src/components/Image/index.native.tsx index f31cfb6936d9..1705f7a2a9fe 100644 --- a/src/components/Image/index.native.js +++ b/src/components/Image/index.native.tsx @@ -1,35 +1,29 @@ import {Image as ImageComponent} from 'expo-image'; -import lodashGet from 'lodash/get'; import React from 'react'; import {withOnyx} from 'react-native-onyx'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import {defaultProps, imagePropTypes} from './imagePropTypes'; -import RESIZE_MODES from './resizeModes'; +import type {ImageOnyxProps, ImageProps} from './types'; const dimensionsCache = new Map(); -function resolveDimensions(key) { - return dimensionsCache.get(key); -} - -function Image(props) { +function Image(props: ImageProps) { // eslint-disable-next-line react/destructuring-assignment const {source, isAuthTokenRequired, session, ...rest} = props; let imageSource = source; - if (source && source.uri && typeof source.uri === 'number') { + if (typeof source === 'object' && 'uri' in source && typeof source.uri === 'number') { imageSource = source.uri; } - if (typeof imageSource !== 'number' && isAuthTokenRequired) { - const authToken = lodashGet(props, 'session.encryptedAuthToken', null); + if (typeof imageSource === 'object' && typeof source === 'object' && isAuthTokenRequired) { + const authToken = props.session?.encryptedAuthToken ?? null; imageSource = { ...source, headers: authToken ? { [CONST.CHAT_ATTACHMENT_TOKEN_KEY]: authToken, } - : null, + : undefined, }; } @@ -49,15 +43,12 @@ function Image(props) { ); } -Image.propTypes = imagePropTypes; -Image.defaultProps = defaultProps; Image.displayName = 'Image'; -const ImageWithOnyx = withOnyx({ + +const ImageWithOnyx = withOnyx({ session: { key: ONYXKEYS.SESSION, }, })(Image); -ImageWithOnyx.resizeMode = RESIZE_MODES; -ImageWithOnyx.resolveDimensions = resolveDimensions; export default ImageWithOnyx; From 7b6ce9856624f3b4d053de522db453890d291cfd Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Tue, 6 Feb 2024 09:32:34 +0100 Subject: [PATCH 025/352] fix onLoad method typing --- src/components/Image/types.ts | 24 +++++++++-------------- src/components/ImageView/index.native.tsx | 2 +- src/components/ImageView/index.tsx | 7 ++++--- src/components/ImageView/types.ts | 7 +------ src/components/Lightbox/index.tsx | 5 ++--- 5 files changed, 17 insertions(+), 28 deletions(-) diff --git a/src/components/Image/types.ts b/src/components/Image/types.ts index aba2b9021541..8cbf3014a0e9 100644 --- a/src/components/Image/types.ts +++ b/src/components/Image/types.ts @@ -10,6 +10,13 @@ type ImageOnyxProps = { session: OnyxEntry; }; +type ImageOnLoadEvent = { + nativeEvent: { + width: number; + height: number; + }; +}; + type ImageOwnProps = { /** Styles for the Image */ style?: StyleProp; @@ -33,12 +40,7 @@ type ImageOwnProps = { onError?: () => void; /** Event for when the image is fully loaded and returns the natural dimensions of the image */ - onLoad?: (event: { - nativeEvent: { - width: number; - height: number; - }; - }) => void; + onLoad?: (event: ImageOnLoadEvent) => void; /** Progress events while the image is downloading */ onProgress?: () => void; @@ -46,12 +48,4 @@ type ImageOwnProps = { type ImageProps = ImageOnyxProps & ImageOwnProps; -export type {ImageOwnProps, ImageOnyxProps, ImageProps, ExpoImageSource}; -type BaseImageProps = { - /** Event called with image dimensions when image is loaded */ - onLoad?: (event: {nativeEvent: {width: number; height: number}}) => void; -}; - -export type {BaseImageProps}; - -export default BaseImageProps; +export type {ImageOwnProps, ImageOnyxProps, ImageProps, ExpoImageSource, ImageOnLoadEvent}; diff --git a/src/components/ImageView/index.native.tsx b/src/components/ImageView/index.native.tsx index 8de1946ef554..127cf4072e27 100644 --- a/src/components/ImageView/index.native.tsx +++ b/src/components/ImageView/index.native.tsx @@ -1,7 +1,7 @@ import React from 'react'; import Lightbox from '@components/Lightbox'; import {DEFAULT_ZOOM_RANGE} from '@components/MultiGestureCanvas'; -import type {ImageViewProps} from './types'; +import type ImageViewProps from './types'; function ImageView({ isAuthTokenRequired = false, diff --git a/src/components/ImageView/index.tsx b/src/components/ImageView/index.tsx index ec37abf6d275..5d09e7abf41d 100644 --- a/src/components/ImageView/index.tsx +++ b/src/components/ImageView/index.tsx @@ -1,17 +1,18 @@ import type {SyntheticEvent} from 'react'; import React, {useCallback, useEffect, useRef, useState} from 'react'; -import type {GestureResponderEvent, LayoutChangeEvent, NativeSyntheticEvent} from 'react-native'; +import type {GestureResponderEvent, LayoutChangeEvent} from 'react-native'; import {View} from 'react-native'; import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import Image from '@components/Image'; import RESIZE_MODES from '@components/Image/resizeModes'; +import type {ImageOnLoadEvent} from '@components/Image/types'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import CONST from '@src/CONST'; import viewRef from '@src/types/utils/viewRef'; -import type {ImageLoadNativeEventData, ImageViewProps} from './types'; +import type ImageViewProps from './types'; type ZoomDelta = {offsetX: number; offsetY: number}; @@ -73,7 +74,7 @@ function ImageView({isAuthTokenRequired = false, url, fileName, onError}: ImageV setIsZoomed(false); }; - const imageLoad = ({nativeEvent}: NativeSyntheticEvent) => { + const imageLoad = ({nativeEvent}: ImageOnLoadEvent) => { setImageRegion(nativeEvent.width, nativeEvent.height); setIsLoading(false); }; diff --git a/src/components/ImageView/types.ts b/src/components/ImageView/types.ts index b85115874a5a..6edd515d7ff8 100644 --- a/src/components/ImageView/types.ts +++ b/src/components/ImageView/types.ts @@ -33,9 +33,4 @@ type ImageViewProps = { zoomRange?: ZoomRange; }; -type ImageLoadNativeEventData = { - width: number; - height: number; -}; - -export type {ImageViewProps, ImageLoadNativeEventData}; +export default ImageViewProps; diff --git a/src/components/Lightbox/index.tsx b/src/components/Lightbox/index.tsx index aeec1876eb93..9eaf3d2969e0 100644 --- a/src/components/Lightbox/index.tsx +++ b/src/components/Lightbox/index.tsx @@ -1,7 +1,8 @@ import React, {useCallback, useEffect, useMemo, useState} from 'react'; -import type {LayoutChangeEvent, NativeSyntheticEvent, StyleProp, ViewStyle} from 'react-native'; +import type {LayoutChangeEvent, StyleProp, ViewStyle} from 'react-native'; import {ActivityIndicator, PixelRatio, StyleSheet, View} from 'react-native'; import Image from '@components/Image'; +import type {ImageOnLoadEvent} from '@components/Image/types'; import MultiGestureCanvas, {DEFAULT_ZOOM_RANGE} from '@components/MultiGestureCanvas'; import type {CanvasSize, ContentSize, OnScaleChangedCallback, ZoomRange} from '@components/MultiGestureCanvas/types'; import {getCanvasFitScale} from '@components/MultiGestureCanvas/utils'; @@ -11,8 +12,6 @@ import NUMBER_OF_CONCURRENT_LIGHTBOXES from './numberOfConcurrentLightboxes'; const DEFAULT_IMAGE_SIZE = 200; const DEFAULT_IMAGE_DIMENSION: ContentSize = {width: DEFAULT_IMAGE_SIZE, height: DEFAULT_IMAGE_SIZE}; -type ImageOnLoadEvent = NativeSyntheticEvent; - const cachedImageDimensions = new Map(); type LightboxProps = { From 6cc06a2ed4e9cff2a598e86cff591b92febb80aa Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Tue, 6 Feb 2024 10:26:37 +0100 Subject: [PATCH 026/352] fix source type mismatches --- src/components/Image/types.ts | 2 +- src/components/ImageWithSizeCalculation.tsx | 2 +- .../ReportActionItem/ReportActionItemImage.tsx | 18 +++++++++++++----- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/components/Image/types.ts b/src/components/Image/types.ts index 8cbf3014a0e9..e764b53706a2 100644 --- a/src/components/Image/types.ts +++ b/src/components/Image/types.ts @@ -22,7 +22,7 @@ type ImageOwnProps = { style?: StyleProp; /** The static asset or URI source of the image */ - source: ExpoImageSource | Omit | ImageRequireSource; + source: ExpoImageSource | Omit | ImageRequireSource | undefined; /** Should an auth token be included in the image request */ isAuthTokenRequired?: boolean; diff --git a/src/components/ImageWithSizeCalculation.tsx b/src/components/ImageWithSizeCalculation.tsx index d0559327274a..e28bc67a9e31 100644 --- a/src/components/ImageWithSizeCalculation.tsx +++ b/src/components/ImageWithSizeCalculation.tsx @@ -73,7 +73,7 @@ function ImageWithSizeCalculation({url, style, onMeasure, isAuthTokenRequired}: { diff --git a/src/components/ReportActionItem/ReportActionItemImage.tsx b/src/components/ReportActionItem/ReportActionItemImage.tsx index 04a99e00c6bf..c8e0cb6f3bf8 100644 --- a/src/components/ReportActionItem/ReportActionItemImage.tsx +++ b/src/components/ReportActionItem/ReportActionItemImage.tsx @@ -1,5 +1,5 @@ import Str from 'expensify-common/lib/str'; -import React from 'react'; +import React, {useMemo} from 'react'; import type {ReactElement} from 'react'; import type {ImageSourcePropType, ViewStyle} from 'react-native'; import {View} from 'react-native'; @@ -49,19 +49,27 @@ type ReportActionItemImageProps = { function ReportActionItemImage({thumbnail, image, enablePreviewModal = false, transaction, canEditReceipt = false, isLocalFile = false, filename}: ReportActionItemImageProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); - const imageSource = tryResolveUrlFromApiRoot(image ?? ''); + const attachmentModalSource = tryResolveUrlFromApiRoot(image ?? ''); const thumbnailSource = tryResolveUrlFromApiRoot(thumbnail ?? ''); const isEReceipt = transaction && TransactionUtils.hasEReceipt(transaction); let receiptImageComponent: ReactElement; + const imageSource = useMemo(() => { + if (thumbnail) { + return typeof thumbnail === 'string' ? {uri: thumbnail} : thumbnail; + } + + return typeof image === 'string' ? {uri: image} : image; + }, [image, thumbnail]); + if (isEReceipt) { receiptImageComponent = ( ); - } else if (thumbnail && !isLocalFile && !Str.isPDF(imageSource as string)) { + } else if (thumbnail && !isLocalFile && !Str.isPDF(attachmentModalSource as string)) { receiptImageComponent = ( ); @@ -84,7 +92,7 @@ function ReportActionItemImage({thumbnail, image, enablePreviewModal = false, tr {({report}) => ( Date: Tue, 6 Feb 2024 15:56:45 +0100 Subject: [PATCH 027/352] migrate react-native.js mock to TypeScript --- .../{react-native.js => react-native.ts} | 44 ++++++++++++++----- 1 file changed, 33 insertions(+), 11 deletions(-) rename __mocks__/{react-native.js => react-native.ts} (63%) diff --git a/__mocks__/react-native.js b/__mocks__/react-native.ts similarity index 63% rename from __mocks__/react-native.js rename to __mocks__/react-native.ts index 1eeea877ca0f..27b78b308446 100644 --- a/__mocks__/react-native.js +++ b/__mocks__/react-native.ts @@ -1,27 +1,47 @@ // eslint-disable-next-line no-restricted-imports import * as ReactNative from 'react-native'; -import _ from 'underscore'; +import type StartupTimer from '@libs/StartupTimer/types'; + +const {BootSplash} = ReactNative.NativeModules; jest.doMock('react-native', () => { let url = 'https://new.expensify.com/'; const getInitialURL = () => Promise.resolve(url); - let appState = 'active'; + let appState: ReactNative.AppStateStatus = 'active'; let count = 0; - const changeListeners = {}; + const changeListeners: Record void> = {}; // Tests will run with the app in a typical small screen size by default. We do this since the react-native test renderer // runs against index.native.js source and so anything that is testing a component reliant on withWindowDimensions() // would be most commonly assumed to be on a mobile phone vs. a tablet or desktop style view. This behavior can be // overridden by explicitly setting the dimensions inside a test via Dimensions.set() - let dimensions = { + let dimensions: Record = { width: 300, height: 700, scale: 1, fontScale: 1, }; - return Object.setPrototypeOf( + type ReactNativeMock = typeof ReactNative & { + NativeModules: typeof ReactNative.NativeModules & { + BootSplash: { + getVisibilityStatus: typeof BootSplash.getVisibilityStatus; + hide: typeof BootSplash.hide; + logoSizeRatio: number; + navigationBarHeight: number; + }; + StartupTimer: StartupTimer; + }; + Linking: typeof ReactNative.Linking & { + setInitialURL: (newUrl: string) => void; + }; + AppState: typeof ReactNative.AppState & { + emitCurrentTestState: (state: ReactNative.AppStateStatus) => void; + }; + }; + + const reactNativeMock: ReactNativeMock = Object.setPrototypeOf( { NativeModules: { ...ReactNative.NativeModules, @@ -36,7 +56,7 @@ jest.doMock('react-native', () => { Linking: { ...ReactNative.Linking, getInitialURL, - setInitialURL(newUrl) { + setInitialURL(newUrl: string) { url = newUrl; }, }, @@ -45,11 +65,11 @@ jest.doMock('react-native', () => { get currentState() { return appState; }, - emitCurrentTestState(state) { + emitCurrentTestState(state: ReactNative.AppStateStatus) { appState = state; - _.each(changeListeners, (listener) => listener(appState)); + Object.entries(changeListeners).forEach(([, listener]) => listener(appState)); }, - addEventListener(type, listener) { + addEventListener(type: ReactNative.AppStateEvent, listener: (state: ReactNative.AppStateStatus) => void) { if (type === 'change') { const originalCount = count; changeListeners[originalCount] = listener; @@ -68,7 +88,7 @@ jest.doMock('react-native', () => { ...ReactNative.Dimensions, addEventListener: jest.fn(), get: () => dimensions, - set: (newDimensions) => { + set: (newDimensions: Record) => { dimensions = newDimensions; }, }, @@ -78,9 +98,11 @@ jest.doMock('react-native', () => { // so it seems easier to just run the callback immediately in tests. InteractionManager: { ...ReactNative.InteractionManager, - runAfterInteractions: (callback) => callback(), + runAfterInteractions: (callback: () => void) => callback(), }, }, ReactNative, ); + + return reactNativeMock; }); From 7ba47c80551e2228985a721bf4da7e95ab22daee Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Wed, 7 Feb 2024 08:28:35 +0100 Subject: [PATCH 028/352] add default props --- src/components/Image/index.native.tsx | 2 +- src/components/Image/index.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Image/index.native.tsx b/src/components/Image/index.native.tsx index 1705f7a2a9fe..8a88d3fd96cb 100644 --- a/src/components/Image/index.native.tsx +++ b/src/components/Image/index.native.tsx @@ -9,7 +9,7 @@ const dimensionsCache = new Map(); function Image(props: ImageProps) { // eslint-disable-next-line react/destructuring-assignment - const {source, isAuthTokenRequired, session, ...rest} = props; + const {source, isAuthTokenRequired = false, session, ...rest} = props; let imageSource = source; if (typeof source === 'object' && 'uri' in source && typeof source.uri === 'number') { diff --git a/src/components/Image/index.tsx b/src/components/Image/index.tsx index 3e3adfac6f4d..89271b6f8a88 100644 --- a/src/components/Image/index.tsx +++ b/src/components/Image/index.tsx @@ -6,7 +6,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {ImageOnyxProps, ImageOwnProps, ImageProps} from './types'; function Image(props: ImageProps) { - const {source: propsSource, isAuthTokenRequired, onLoad, session, ...forwardedProps} = props; + const {source: propsSource, isAuthTokenRequired = false, onLoad, session, ...forwardedProps} = props; /** * Check if the image source is a URL - if so the `encryptedAuthToken` is appended * to the source. From 8ea6ebcc0090a2db304011297e43af3edc7ecc59 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Fri, 9 Feb 2024 13:27:09 +0100 Subject: [PATCH 029/352] [TS migration] Migrate 'format.js' test --- .../compare/output/{format.js => format.ts} | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) rename tests/e2e/compare/output/{format.js => format.ts} (71%) diff --git a/tests/e2e/compare/output/format.js b/tests/e2e/compare/output/format.ts similarity index 71% rename from tests/e2e/compare/output/format.js rename to tests/e2e/compare/output/format.ts index c31ac547b41d..e0f503f12187 100644 --- a/tests/e2e/compare/output/format.js +++ b/tests/e2e/compare/output/format.ts @@ -3,12 +3,28 @@ * from: https://github.com/callstack/reassure/blob/main/packages/reassure-compare/src/utils/format.ts */ -const formatPercent = (value) => { +type Stats = { + mean: number; + stdev: number; + runs: number; + entries: Record; +}; + +type CompareEntry = { + name: string; + baseline: Stats; + current: Stats; + diff: number; + relativeDurationDiff: number; + isDurationDiffOfSignificance: boolean; +}; + +const formatPercent = (value: number): string => { const valueAsPercent = value * 100; return `${valueAsPercent.toFixed(1)}%`; }; -const formatPercentChange = (value) => { +const formatPercentChange = (value: number): string => { const absValue = Math.abs(value); // Round to zero @@ -19,9 +35,9 @@ const formatPercentChange = (value) => { return `${value >= 0 ? '+' : '-'}${formatPercent(absValue)}`; }; -const formatDuration = (duration) => `${duration.toFixed(3)} ms`; +const formatDuration = (duration: number): string => `${duration.toFixed(3)} ms`; -const formatDurationChange = (value) => { +const formatDurationChange = (value: number): string => { if (value > 0) { return `+${formatDuration(value)}`; } @@ -31,7 +47,7 @@ const formatDurationChange = (value) => { return '0 ms'; }; -const formatChange = (value) => { +const formatChange = (value: number): string => { if (value > 0) { return `+${value}`; } @@ -41,7 +57,7 @@ const formatChange = (value) => { return '0'; }; -const getDurationSymbols = (entry) => { +const getDurationSymbols = (entry: CompareEntry): string => { if (!entry.isDurationDiffOfSignificance) { if (entry.relativeDurationDiff > 0.15) { return '🟡'; @@ -68,7 +84,7 @@ const getDurationSymbols = (entry) => { return ''; }; -const formatDurationDiffChange = (entry) => { +const formatDurationDiffChange = (entry: CompareEntry): string => { const {baseline, current} = entry; let output = `${formatDuration(baseline.mean)} → ${formatDuration(current.mean)}`; @@ -82,7 +98,7 @@ const formatDurationDiffChange = (entry) => { return output; }; -module.exports = { +export default { formatPercent, formatPercentChange, formatDuration, From 25f4d94cda0c60240382a9f37a03bdecff1eab49 Mon Sep 17 00:00:00 2001 From: Georgia Monahan Date: Fri, 9 Feb 2024 10:59:37 -0800 Subject: [PATCH 030/352] Fix text styling/gaps on ReportPreview/MoneyRequestPreview --- .../ReportActionItem/MoneyRequestPreview.tsx | 128 +++++++++--------- .../ReportActionItem/ReportPreview.tsx | 124 +++++++++-------- src/styles/index.ts | 17 ++- 3 files changed, 147 insertions(+), 122 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestPreview.tsx b/src/components/ReportActionItem/MoneyRequestPreview.tsx index e89193108d24..2aa2c0047d64 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview.tsx +++ b/src/components/ReportActionItem/MoneyRequestPreview.tsx @@ -275,70 +275,76 @@ function MoneyRequestPreview({ {isEmptyObject(transaction) && !ReportActionsUtils.isMessageDeleted(action) && action.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE ? ( ) : ( - - - - {getPreviewHeaderText() + (isSettled && !iouReport?.isCancelledIOU ? ` • ${getSettledMessage()}` : '')} - - {!isSettled && hasFieldErrors && ( - - )} - - - - - {displayAmount} - - {ReportUtils.isSettled(iouReport?.reportID) && !isBillSplit && ( - - + + + + + + {getPreviewHeaderText() + (isSettled && !iouReport?.isCancelledIOU ? ` • ${getSettledMessage()}` : '')} + + {!isSettled && hasFieldErrors && ( + + )} + + + + + + {displayAmount} + + {ReportUtils.isSettled(iouReport?.reportID) && !isBillSplit && ( + + + + )} + + {isBillSplit && ( + + + + )} + + + + {!isCurrentUserManager && shouldShowPendingConversionMessage && ( + {translate('iou.pendingConversionMessage')} + )} + {shouldShowDescription && } + {shouldShowMerchant && {merchantOrDescription}} + + {isBillSplit && participantAccountIDs.length > 0 && !!requestAmount && requestAmount > 0 && ( + + {translate('iou.amountEach', { + amount: CurrencyUtils.convertToDisplayString( + IOUUtils.calculateAmount(isPolicyExpenseChat ? 1 : participantAccountIDs.length - 1, requestAmount, requestCurrency ?? ''), + requestCurrency, + ), + })} + + )} + - )} - - {isBillSplit && ( - - - )} - - - - {!isCurrentUserManager && shouldShowPendingConversionMessage && ( - {translate('iou.pendingConversionMessage')} - )} - {shouldShowDescription && } - {shouldShowMerchant && {merchantOrDescription}} - {isBillSplit && participantAccountIDs.length > 0 && !!requestAmount && requestAmount > 0 && ( - - {translate('iou.amountEach', { - amount: CurrencyUtils.convertToDisplayString( - IOUUtils.calculateAmount(isPolicyExpenseChat ? 1 : participantAccountIDs.length - 1, requestAmount, requestCurrency ?? ''), - requestCurrency, - ), - })} - - )} - )} diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 4066776711b1..caa443dba2e9 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -269,70 +269,76 @@ function ReportPreview({ size={CONST.RECEIPT.MAX_REPORT_PREVIEW_RECEIPTS} /> )} - - - - {getPreviewMessage()} - - {!iouSettled && hasErrors && ( - - )} - - - - {getDisplayAmount()} - {ReportUtils.isSettled(iouReportID) && ( - + + + + + + {getPreviewMessage()} + + {!iouSettled && hasErrors && ( + )} + + + + + {getDisplayAmount()} + {ReportUtils.isSettled(iouReportID) && ( + + + + )} + + + {shouldShowSubtitle && ( + + + {previewSubtitle || moneyRequestComment} + + + )} + + {shouldShowSettlementButton && ( + chatReport && iouReport && paymentType && IOU.payMoneyRequest(paymentType, chatReport, iouReport)} + enablePaymentsRoute={ROUTES.ENABLE_PAYMENTS} + addBankAccountRoute={bankAccountRoute} + shouldHidePaymentOptions={!shouldShowPayButton} + shouldShowApproveButton={shouldShowApproveButton} + style={[styles.mt3]} + kycWallAnchorAlignment={{ + horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, + vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM, + }} + paymentMethodDropdownAnchorAlignment={{ + horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT, + vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM, + }} + isDisabled={!canAllowSettlement} + /> + )} + {shouldShowSubmitButton && ( +