From b8cc6296721501f486858eb43d065a711930509d Mon Sep 17 00:00:00 2001 From: Wildan M Date: Fri, 1 Nov 2024 16:27:08 +0700 Subject: [PATCH 001/240] Fix old data briefly appeared --- src/pages/workspace/categories/EditCategoryPage.tsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/pages/workspace/categories/EditCategoryPage.tsx b/src/pages/workspace/categories/EditCategoryPage.tsx index 330c47f0197f..0892b91fbdd3 100644 --- a/src/pages/workspace/categories/EditCategoryPage.tsx +++ b/src/pages/workspace/categories/EditCategoryPage.tsx @@ -49,11 +49,14 @@ function EditCategoryPage({route}: EditCategoryPageProps) { if (currentCategoryName !== newCategoryName) { Category.renamePolicyCategory(route.params.policyID, {oldName: currentCategoryName, newName: values.categoryName}); } - Navigation.goBack( - isQuickSettingsFlow - ? ROUTES.SETTINGS_CATEGORY_SETTINGS.getRoute(route.params.policyID, route.params.categoryName, backTo) - : ROUTES.WORKSPACE_CATEGORY_SETTINGS.getRoute(route.params.policyID, route.params.categoryName), - ); + + Navigation.setNavigationActionToMicrotaskQueue(()=>{ + Navigation.goBack( + isQuickSettingsFlow + ? ROUTES.SETTINGS_CATEGORY_SETTINGS.getRoute(route.params.policyID, route.params.categoryName, backTo) + : ROUTES.WORKSPACE_CATEGORY_SETTINGS.getRoute(route.params.policyID, route.params.categoryName), + ); + }); }, [isQuickSettingsFlow, currentCategoryName, route.params.categoryName, route.params.policyID, backTo], ); From 1d2852912f3babb03378465af2942f9f41826ad0 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Tue, 12 Nov 2024 13:03:44 +0100 Subject: [PATCH 002/240] upgrade react-native-vision-camera --- ios/Podfile.lock | 27 ++++++--------------------- package-lock.json | 14 ++++++++++++-- package.json | 2 +- 3 files changed, 19 insertions(+), 24 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 6494782a6ec0..b9872d857574 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -2695,27 +2695,12 @@ PODS: - SDWebImage/Core (~> 5.17) - SocketRocket (0.7.0) - Turf (2.8.0) - - VisionCamera (4.0.0-beta.13): - - DoubleConversion - - glog - - hermes-engine - - RCT-Folly (= 2024.01.01.00) - - RCTRequired - - RCTTypeSafety + - VisionCamera (4.6.1): + - VisionCamera/Core (= 4.6.1) + - VisionCamera/React (= 4.6.1) + - VisionCamera/Core (4.6.1) + - VisionCamera/React (4.6.1): - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-ImageManager - - React-NativeModulesApple - - React-RCTFabric - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - Yoga - Yoga (0.0.0) DEPENDENCIES: @@ -3280,7 +3265,7 @@ SPEC CHECKSUMS: SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380 SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d Turf: aa2ede4298009639d10db36aba1a7ebaad072a5e - VisionCamera: c6c8aa4b028501fc87644550fbc35a537d4da3fb + VisionCamera: c95a8ad535f527562be1fb05fb2fd324578e769c Yoga: a1d7895431387402a674fd0d1c04ec85e87909b8 PODFILE CHECKSUM: 15e2f095b9c80d658459723edf84005a6867debf diff --git a/package-lock.json b/package-lock.json index f2dbe85646d1..ce4559478107 100644 --- a/package-lock.json +++ b/package-lock.json @@ -115,7 +115,7 @@ "react-native-tab-view": "^3.5.2", "react-native-url-polyfill": "^2.0.0", "react-native-view-shot": "3.8.0", - "react-native-vision-camera": "4.0.0-beta.13", + "react-native-vision-camera": "^4.6.1", "react-native-web": "0.19.13", "react-native-webview": "13.8.6", "react-plaid-link": "3.3.2", @@ -35812,14 +35812,24 @@ } }, "node_modules/react-native-vision-camera": { - "version": "4.0.0-beta.13", + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/react-native-vision-camera/-/react-native-vision-camera-4.6.1.tgz", + "integrity": "sha512-USp7g+Q/H7nzIS2XBJTWVdzZArxgZu+IFafgswVzxdmr0iSpLjLUtoUp+SUWxZ+nLhJriYYvqg8hfZrJtnpnlw==", "license": "MIT", "peerDependencies": { + "@shopify/react-native-skia": "*", "react": "*", "react-native": "*", + "react-native-reanimated": "*", "react-native-worklets-core": "*" }, "peerDependenciesMeta": { + "@shopify/react-native-skia": { + "optional": true + }, + "react-native-reanimated": { + "optional": true + }, "react-native-worklets-core": { "optional": true } diff --git a/package.json b/package.json index 2089a372a4d4..4d637e0cf432 100644 --- a/package.json +++ b/package.json @@ -172,7 +172,7 @@ "react-native-tab-view": "^3.5.2", "react-native-url-polyfill": "^2.0.0", "react-native-view-shot": "3.8.0", - "react-native-vision-camera": "4.0.0-beta.13", + "react-native-vision-camera": "^4.6.1", "react-native-web": "0.19.13", "react-native-webview": "13.8.6", "react-plaid-link": "3.3.2", From 33511a145e297d67eb680a7114b3683517574b99 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Tue, 12 Nov 2024 16:56:59 +0100 Subject: [PATCH 003/240] draft folder receipt creation --- .../step/IOURequestStepScan/index.native.tsx | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx index f7e575b898fd..ea99d2cf0151 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx @@ -2,6 +2,7 @@ import {useFocusEffect} from '@react-navigation/core'; import {Str} from 'expensify-common'; import React, {useCallback, useMemo, useRef, useState} from 'react'; import {ActivityIndicator, Alert, AppState, InteractionManager, View} from 'react-native'; +import ReactNativeBlobUtil from 'react-native-blob-util'; import {Gesture, GestureDetector} from 'react-native-gesture-handler'; import {useOnyx} from 'react-native-onyx'; import {RESULTS} from 'react-native-permissions'; @@ -494,12 +495,44 @@ function IOURequestStepScan({ setDidCapturePhoto(true); + console.debug('[dev] Taking photo'); + console.debug('[dev] ReactNativeBlobUtil.fs.dirs.DocumentDir', ReactNativeBlobUtil.fs.dirs.DocumentDir); + + const path = `${ReactNativeBlobUtil.fs.dirs.DocumentDir}/Receipts-Pending-Upload`; + + console.debug('ReactNativeBlobUtil.fs.isDir(path)', ReactNativeBlobUtil.fs.isDir(path)); + + ReactNativeBlobUtil.fs + .isDir(path) + .then((isDir) => { + console.debug('[dev] ReactNativeBlobUtil.fs.isDir:', isDir); + if (isDir) { + return; + } + + ReactNativeBlobUtil.fs + .mkdir(path) + .then(() => { + console.debug('[dev] Directory created successfully'); + }) + .catch((error) => { + console.error('[dev] Error creating directory:', error); + }); + }) + .catch((error) => { + console.error('[dev] Error checking if directory exists:', error); + }); + camera?.current ?.takePhoto({ flash: flash && hasFlash ? 'on' : 'off', enableShutterSound: !isPlatformMuted, + path, }) .then((photo: PhotoFile) => { + console.debug('[dev] photo', photo); + return; + // Store the receipt on the transaction object in Onyx const source = getPhotoSource(photo.path); IOU.setMoneyRequestReceipt(transactionID, source, photo.path, !isEditing); @@ -529,14 +562,14 @@ function IOURequestStepScan({ () => { setDidCapturePhoto(false); showCameraAlert(); - Log.warn('Error reading photo'); + Log.warn('[dev] Error reading photo'); }, ); }) .catch((error: string) => { setDidCapturePhoto(false); showCameraAlert(); - Log.warn('Error taking photo', error); + Log.warn('[dev] Error taking photo', error); }); }, [ cameraPermissionStatus, From 7647b6d386a90740e6a6a7c00560ccee0c985cd7 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Wed, 13 Nov 2024 11:23:37 +0100 Subject: [PATCH 004/240] use outputOrientation --- src/pages/iou/request/step/IOURequestStepScan/index.native.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx index ea99d2cf0151..9f342ed7b0ea 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx @@ -662,7 +662,7 @@ function IOURequestStepScan({ zoom={device.neutralZoom} photo cameraTabIndex={1} - orientation="portrait" + outputOrientation="preview" /> From fd6168b77867db70694cf71901f57e60d12a581e Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Wed, 13 Nov 2024 13:01:10 +0100 Subject: [PATCH 005/240] integrate getReceiptsUploadFolderPath --- src/CONST.ts | 1 + src/libs/getReceiptsUploadFolderPath/index.android.ts | 7 +++++++ src/libs/getReceiptsUploadFolderPath/index.ios.ts | 7 +++++++ src/libs/getReceiptsUploadFolderPath/index.ts | 5 +++++ src/libs/getReceiptsUploadFolderPath/types.ts | 3 +++ 5 files changed, 23 insertions(+) create mode 100644 src/libs/getReceiptsUploadFolderPath/index.android.ts create mode 100644 src/libs/getReceiptsUploadFolderPath/index.ios.ts create mode 100644 src/libs/getReceiptsUploadFolderPath/index.ts create mode 100644 src/libs/getReceiptsUploadFolderPath/types.ts diff --git a/src/CONST.ts b/src/CONST.ts index e95e3d4a5603..657135a238f6 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -5845,6 +5845,7 @@ const CONST = { DOWNLOADS_PATH: '/Downloads', DOWNLOADS_TIMEOUT: 5000, NEW_EXPENSIFY_PATH: '/New Expensify', + RECEIPTS_UPLOAD_PATH: '/Receipts-Pending-Upload', ENVIRONMENT_SUFFIX: { DEV: ' Dev', diff --git a/src/libs/getReceiptsUploadFolderPath/index.android.ts b/src/libs/getReceiptsUploadFolderPath/index.android.ts new file mode 100644 index 000000000000..5179afd64134 --- /dev/null +++ b/src/libs/getReceiptsUploadFolderPath/index.android.ts @@ -0,0 +1,7 @@ +import ReactNativeBlobUtil from 'react-native-blob-util'; +import CONST from '@src/CONST'; +import type GetReceiptsUploadFolderPath from './types'; + +const getReceiptsUploadFolderPath: GetReceiptsUploadFolderPath = () => `${ReactNativeBlobUtil.fs.dirs.DownloadDir}${CONST.RECEIPTS_UPLOAD_PATH}`; + +export default getReceiptsUploadFolderPath; diff --git a/src/libs/getReceiptsUploadFolderPath/index.ios.ts b/src/libs/getReceiptsUploadFolderPath/index.ios.ts new file mode 100644 index 000000000000..c787909055a6 --- /dev/null +++ b/src/libs/getReceiptsUploadFolderPath/index.ios.ts @@ -0,0 +1,7 @@ +import ReactNativeBlobUtil from 'react-native-blob-util'; +import CONST from '@src/CONST'; +import type GetReceiptsUploadFolderPath from './types'; + +const getReceiptsUploadFolderPath: GetReceiptsUploadFolderPath = () => `${ReactNativeBlobUtil.fs.dirs.DocumentDir}${CONST.RECEIPTS_UPLOAD_PATH}`; + +export default getReceiptsUploadFolderPath; diff --git a/src/libs/getReceiptsUploadFolderPath/index.ts b/src/libs/getReceiptsUploadFolderPath/index.ts new file mode 100644 index 000000000000..fcec3b2d6711 --- /dev/null +++ b/src/libs/getReceiptsUploadFolderPath/index.ts @@ -0,0 +1,5 @@ +import type GetReceiptsUploadFolderPath from './types'; + +const getReceiptsUploadFolderPath: GetReceiptsUploadFolderPath = () => ''; + +export default getReceiptsUploadFolderPath; diff --git a/src/libs/getReceiptsUploadFolderPath/types.ts b/src/libs/getReceiptsUploadFolderPath/types.ts new file mode 100644 index 000000000000..44c21e513f4d --- /dev/null +++ b/src/libs/getReceiptsUploadFolderPath/types.ts @@ -0,0 +1,3 @@ +type GetReceiptsUploadFolderPath = () => string; + +export default GetReceiptsUploadFolderPath; From be154dbf5ce671dfe0df92536a760fddd4488cc6 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Wed, 13 Nov 2024 16:07:36 +0100 Subject: [PATCH 006/240] get new flow workable --- .../step/IOURequestStepScan/index.native.tsx | 97 ++++++++++--------- 1 file changed, 49 insertions(+), 48 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx index 9f342ed7b0ea..5bf50419bcbc 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx @@ -32,6 +32,7 @@ import * as FileUtils from '@libs/fileDownload/FileUtils'; import getPhotoSource from '@libs/fileDownload/getPhotoSource'; import getCurrentPosition from '@libs/getCurrentPosition'; import getPlatform from '@libs/getPlatform'; +import getReceiptsUploadFolderPath from '@libs/getReceiptsUploadFolderPath'; import * as IOUUtils from '@libs/IOUUtils'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; @@ -496,16 +497,16 @@ function IOURequestStepScan({ setDidCapturePhoto(true); console.debug('[dev] Taking photo'); - console.debug('[dev] ReactNativeBlobUtil.fs.dirs.DocumentDir', ReactNativeBlobUtil.fs.dirs.DocumentDir); - const path = `${ReactNativeBlobUtil.fs.dirs.DocumentDir}/Receipts-Pending-Upload`; + const path = getReceiptsUploadFolderPath(); - console.debug('ReactNativeBlobUtil.fs.isDir(path)', ReactNativeBlobUtil.fs.isDir(path)); + console.debug('[dev] path', path); ReactNativeBlobUtil.fs .isDir(path) .then((isDir) => { - console.debug('[dev] ReactNativeBlobUtil.fs.isDir:', isDir); + console.debug('[dev] Checking if directory exists', isDir); + if (isDir) { return; } @@ -521,55 +522,55 @@ function IOURequestStepScan({ }) .catch((error) => { console.error('[dev] Error checking if directory exists:', error); - }); - - camera?.current - ?.takePhoto({ - flash: flash && hasFlash ? 'on' : 'off', - enableShutterSound: !isPlatformMuted, - path, }) - .then((photo: PhotoFile) => { - console.debug('[dev] photo', photo); - return; - - // Store the receipt on the transaction object in Onyx - const source = getPhotoSource(photo.path); - IOU.setMoneyRequestReceipt(transactionID, source, photo.path, !isEditing); - - FileUtils.readFileAsync( - source, - photo.path, - (file) => { - if (isEditing) { - updateScanAndNavigate(file, source); - return; - } - if (shouldSkipConfirmation) { - setFileResize(file); - setFileSource(source); - const gpsRequired = transaction?.amount === 0 && iouType !== CONST.IOU.TYPE.SPLIT && file; - if (gpsRequired) { - const shouldStartLocationPermissionFlow = IOUUtils.shouldStartLocationPermissionFlow(); - if (shouldStartLocationPermissionFlow) { - setStartLocationPermissionFlow(true); + .then(() => { + camera?.current + ?.takePhoto({ + flash: flash && hasFlash ? 'on' : 'off', + enableShutterSound: !isPlatformMuted, + path, + }) + .then((photo: PhotoFile) => { + console.debug('[dev] photo', photo); + + // Store the receipt on the transaction object in Onyx + const source = getPhotoSource(photo.path); + IOU.setMoneyRequestReceipt(transactionID, source, photo.path, !isEditing); + + FileUtils.readFileAsync( + source, + photo.path, + (file) => { + if (isEditing) { + updateScanAndNavigate(file, source); return; } - } - } - navigateToConfirmationStep(file, source, false); - }, - () => { + if (shouldSkipConfirmation) { + setFileResize(file); + setFileSource(source); + const gpsRequired = transaction?.amount === 0 && iouType !== CONST.IOU.TYPE.SPLIT && file; + if (gpsRequired) { + const shouldStartLocationPermissionFlow = IOUUtils.shouldStartLocationPermissionFlow(); + if (shouldStartLocationPermissionFlow) { + setStartLocationPermissionFlow(true); + return; + } + } + } + navigateToConfirmationStep(file, source, false); + }, + () => { + setDidCapturePhoto(false); + showCameraAlert(); + Log.warn('[dev] Error reading photo'); + }, + ); + }) + .catch((error: string) => { setDidCapturePhoto(false); showCameraAlert(); - Log.warn('[dev] Error reading photo'); - }, - ); - }) - .catch((error: string) => { - setDidCapturePhoto(false); - showCameraAlert(); - Log.warn('[dev] Error taking photo', error); + Log.warn('[dev] Error taking photo', error); + }); }); }, [ cameraPermissionStatus, From b2b25b7bec5555b5fc98c3d07a97b739f4117216 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Sat, 16 Nov 2024 18:47:05 +0700 Subject: [PATCH 007/240] Clean pending action deletePolicyDistanceRates when success --- src/libs/actions/Policy/DistanceRate.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/Policy/DistanceRate.ts b/src/libs/actions/Policy/DistanceRate.ts index 64efdaf732c8..786e22ef91d9 100644 --- a/src/libs/actions/Policy/DistanceRate.ts +++ b/src/libs/actions/Policy/DistanceRate.ts @@ -499,7 +499,10 @@ function deletePolicyDistanceRates(policyID: string, customUnit: CustomUnit, rat }; } else { optimisticRates[rateID] = currentRates[rateID]; - successRates[rateID] = currentRates[rateID]; + successRates[rateID] = { + ...currentRates[rateID], + pendingAction: null, + }; } } From d7159314bd7b9f197ebbb77eccab07560beef31c Mon Sep 17 00:00:00 2001 From: NJ-2020 Date: Sun, 17 Nov 2024 15:46:56 -0800 Subject: [PATCH 008/240] fix invitation messageis converted to uni message --- src/libs/actions/Policy/Member.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/Policy/Member.ts b/src/libs/actions/Policy/Member.ts index 8fb551cdec81..4c3d20021c6a 100644 --- a/src/libs/actions/Policy/Member.ts +++ b/src/libs/actions/Policy/Member.ts @@ -676,7 +676,9 @@ function addMembersToWorkspace(invitedEmailsToAccountIDs: InvitedEmailsToAccount employees: JSON.stringify(logins.map((login) => ({email: login}))), ...(optimisticAnnounceChat.announceChatReportID ? {announceChatReportID: optimisticAnnounceChat.announceChatReportID} : {}), ...(optimisticAnnounceChat.announceChatReportActionID ? {announceCreatedReportActionID: optimisticAnnounceChat.announceChatReportActionID} : {}), - welcomeNote: Parser.replace(welcomeNote), + welcomeNote: Parser.replace(welcomeNote, { + shouldEscapeText: false, + }), policyID, }; if (!isEmptyObject(membersChats.reportCreationData)) { From 9b517d4bf2bdb162615aceb318049e12c3105fa5 Mon Sep 17 00:00:00 2001 From: Tomasz Lesniakiewicz Date: Thu, 21 Nov 2024 16:14:13 +0100 Subject: [PATCH 009/240] fix: remove duplicates when report is same as policy --- src/components/ParentNavigationSubtitle.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/ParentNavigationSubtitle.tsx b/src/components/ParentNavigationSubtitle.tsx index 997106f3e649..9edf73e5f36f 100644 --- a/src/components/ParentNavigationSubtitle.tsx +++ b/src/components/ParentNavigationSubtitle.tsx @@ -61,7 +61,9 @@ function ParentNavigationSubtitle({parentNavigationSubtitleData, parentReportAct {reportName} )} - {!!workspaceName && {` ${translate('threads.in')} ${workspaceName}`}} + {workspaceName && workspaceName !== reportName && ( + {` ${translate('threads.in')} ${workspaceName}`} + )} ); From cab51e896dfeb59cdb12c7b0b5c3c81378212a1e Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Thu, 21 Nov 2024 16:29:27 +0100 Subject: [PATCH 010/240] remove .defaultProps and related code from the library --- patches/react-native-render-html+6.3.1.patch | 740 +++++++++++++++++++ 1 file changed, 740 insertions(+) diff --git a/patches/react-native-render-html+6.3.1.patch b/patches/react-native-render-html+6.3.1.patch index 6958556d5a96..0d2c2496f568 100644 --- a/patches/react-native-render-html+6.3.1.patch +++ b/patches/react-native-render-html+6.3.1.patch @@ -1,3 +1,339 @@ +diff --git a/node_modules/react-native-render-html/lib/commonjs/TChildrenRenderer.js b/node_modules/react-native-render-html/lib/commonjs/TChildrenRenderer.js +index 9d16738..bbc66a0 100644 +--- a/node_modules/react-native-render-html/lib/commonjs/TChildrenRenderer.js ++++ b/node_modules/react-native-render-html/lib/commonjs/TChildrenRenderer.js +@@ -3,7 +3,7 @@ + Object.defineProperty(exports, "__esModule", { + value: true + }); +-exports.default = exports.tchildrenRendererDefaultProps = void 0; ++exports.default = void 0; + + var _renderChildren = _interopRequireDefault(require("./renderChildren")); + +@@ -15,15 +15,6 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de + */ + const TChildrenRenderer = _renderChildren.default.bind(null); + +-const tchildrenRendererDefaultProps = { +- propsForChildren: {} +-}; +-/** +- * @ignore +- */ +- +-exports.tchildrenRendererDefaultProps = tchildrenRendererDefaultProps; +-TChildrenRenderer.defaultProps = tchildrenRendererDefaultProps; + var _default = TChildrenRenderer; + exports.default = _default; + //# sourceMappingURL=TChildrenRenderer.js.map +\ No newline at end of file +diff --git a/node_modules/react-native-render-html/lib/commonjs/TChildrenRenderer.js.map b/node_modules/react-native-render-html/lib/commonjs/TChildrenRenderer.js.map +index 3fdd160..50e52b5 100644 +--- a/node_modules/react-native-render-html/lib/commonjs/TChildrenRenderer.js.map ++++ b/node_modules/react-native-render-html/lib/commonjs/TChildrenRenderer.js.map +@@ -1 +1 @@ +-{"version":3,"sources":["TChildrenRenderer.tsx"],"names":["TChildrenRenderer","renderChildren","bind","tchildrenRendererDefaultProps","propsForChildren","defaultProps"],"mappings":";;;;;;;AAEA;;;;AAEA;AACA;AACA;AACA;AACA,MAAMA,iBAA4D,GAChEC,wBAAeC,IAAf,CAAoB,IAApB,CADF;;AAGO,MAAMC,6BAGZ,GAAG;AACFC,EAAAA,gBAAgB,EAAE;AADhB,CAHG;AAOP;AACA;AACA;;;AACAJ,iBAAiB,CAACK,YAAlB,GAAiCF,6BAAjC;eAEeH,iB","sourcesContent":["import { FunctionComponent } from 'react';\nimport { TChildrenRendererProps } from './shared-types';\nimport renderChildren from './renderChildren';\n\n/**\n * A component to render collections of tnodes.\n * Especially useful when used with {@link useTNodeChildrenProps}.\n */\nconst TChildrenRenderer: FunctionComponent =\n renderChildren.bind(null);\n\nexport const tchildrenRendererDefaultProps: Pick<\n TChildrenRendererProps,\n 'propsForChildren'\n> = {\n propsForChildren: {}\n};\n\n/**\n * @ignore\n */\nTChildrenRenderer.defaultProps = tchildrenRendererDefaultProps;\n\nexport default TChildrenRenderer;\n"]} +\ No newline at end of file ++{"version":3,"sources":["TChildrenRenderer.tsx"],"names":["TChildrenRenderer","renderChildren","bind"],"mappings":";;;;;;;AAEA;;;;AAEA;AACA;AACA;AACA;AACA,MAAMA,iBAA4D,GAChEC,wBAAeC,IAAf,CAAoB,IAApB,CADF;;eAGeF,iB","sourcesContent":["import { FunctionComponent } from 'react';\nimport { TChildrenRendererProps } from './shared-types';\nimport renderChildren from './renderChildren';\n\n/**\n * A component to render collections of tnodes.\n * Especially useful when used with {@link useTNodeChildrenProps}.\n */\nconst TChildrenRenderer: FunctionComponent =\n renderChildren.bind(null);\n\nexport default TChildrenRenderer;\n"]} +\ No newline at end of file +diff --git a/node_modules/react-native-render-html/lib/commonjs/TNodeChildrenRenderer.js b/node_modules/react-native-render-html/lib/commonjs/TNodeChildrenRenderer.js +index 50b43ca..5ecf4a4 100644 +--- a/node_modules/react-native-render-html/lib/commonjs/TNodeChildrenRenderer.js ++++ b/node_modules/react-native-render-html/lib/commonjs/TNodeChildrenRenderer.js +@@ -8,8 +8,6 @@ exports.default = void 0; + + var _SharedPropsProvider = require("./context/SharedPropsProvider"); + +-var _TChildrenRenderer = require("./TChildrenRenderer"); +- + var _renderChildren = _interopRequireDefault(require("./renderChildren")); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +@@ -78,12 +76,7 @@ function TNodeChildrenRenderer(props) { + + return (0, _renderChildren.default)(useTNodeChildrenProps(props)); + } +-/** +- * @ignore +- */ +- + +-TNodeChildrenRenderer.defaultProps = _TChildrenRenderer.tchildrenRendererDefaultProps; + var _default = TNodeChildrenRenderer; + exports.default = _default; + //# sourceMappingURL=TNodeChildrenRenderer.js.map +\ No newline at end of file +diff --git a/node_modules/react-native-render-html/lib/commonjs/TNodeChildrenRenderer.js.map b/node_modules/react-native-render-html/lib/commonjs/TNodeChildrenRenderer.js.map +index 2a7c4b6..ba13ece 100644 +--- a/node_modules/react-native-render-html/lib/commonjs/TNodeChildrenRenderer.js.map ++++ b/node_modules/react-native-render-html/lib/commonjs/TNodeChildrenRenderer.js.map +@@ -1 +1 @@ +-{"version":3,"sources":["TNodeChildrenRenderer.tsx"],"names":["isCollapsible","tnode","type","useTNodeChildrenProps","propsForChildren","disableMarginCollapsing","renderChild","enableExperimentalMarginCollapsing","shouldCollapseChildren","tchildren","children","TNodeChildrenRenderer","props","data","defaultProps","tchildrenRendererDefaultProps"],"mappings":";;;;;;;;AAEA;;AACA;;AAKA;;;;AAEA,SAASA,aAAT,CAAuBC,KAAvB,EAAqC;AACnC,SAAOA,KAAK,CAACC,IAAN,KAAe,OAAf,IAA0BD,KAAK,CAACC,IAAN,KAAe,UAAhD;AACD;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AACO,SAASC,qBAAT,CAA+B;AACpCF,EAAAA,KADoC;AAEpCG,EAAAA,gBAFoC;AAGpCC,EAAAA,uBAAuB,GAAG,KAHU;AAIpCC,EAAAA;AAJoC,CAA/B,EAKgD;AACrD,QAAM;AAAEC,IAAAA;AAAF,MAAyC,0CAA/C;AACA,QAAMC,sBAAsB,GAC1BD,kCAAkC,IAClC,CAACF,uBADD,IAEAL,aAAa,CAACC,KAAD,CAHf;AAIA,SAAO;AACLG,IAAAA,gBADK;AAELC,IAAAA,uBAAuB,EAAE,CAACG,sBAFrB;AAGLC,IAAAA,SAAS,EAAER,KAAK,CAACS,QAHZ;AAILJ,IAAAA;AAJK,GAAP;AAMD;AAED;AACA;AACA;;;AACA,SAASK,qBAAT,CACEC,KADF,EAEgB;AACd,MAAIA,KAAK,CAACX,KAAN,CAAYC,IAAZ,KAAqB,MAAzB,EAAiC;AAC/B;AACA,WAAOU,KAAK,CAACX,KAAN,CAAYY,IAAnB;AACD,GAJa,CAKd;AACA;AACA;;;AACA,SAAO,6BAAeV,qBAAqB,CAACS,KAAD,CAApC,CAAP;AACD;AAED;AACA;AACA;;;AACAD,qBAAqB,CAACG,YAAtB,GAAqCC,gDAArC;eAEeJ,qB","sourcesContent":["import { ReactElement } from 'react';\nimport { TNode } from '@native-html/transient-render-engine';\nimport { useSharedProps } from './context/SharedPropsProvider';\nimport { tchildrenRendererDefaultProps } from './TChildrenRenderer';\nimport {\n TChildrenRendererProps,\n TNodeChildrenRendererProps\n} from './shared-types';\nimport renderChildren from './renderChildren';\n\nfunction isCollapsible(tnode: TNode) {\n return tnode.type === 'block' || tnode.type === 'phrasing';\n}\n\n/**\n * A hook especially useful when one need to tamper with children in a custom\n * renderer. Should be used with {@link TChildrenRenderer}.\n *\n * @example\n * For example, a custom renderer which inserts ads in an article:\n *\n * ```tsx\n * function ArticleRenderer(props) {\n * const { tnode, TDefaultRenderer, ...defaultRendererProps } = props;\n * const tchildrenProps = useTNodeChildrenProps(props);\n * const firstChildrenChunk = tnode.children.slice(0, 2);\n * const secondChildrenChunk = tnode.children.slice(2, 4);\n * const thirdChildrenChunk = tnode.children.slice(4, 5);\n * return (\n * \n * \n * {firstChildrenChunk.length === 2 ? : null}\n * \n * {secondChildrenChunk.length === 2 ? : null}\n * \n * \n * );\n * };\n * ```\n */\nexport function useTNodeChildrenProps({\n tnode,\n propsForChildren,\n disableMarginCollapsing = false,\n renderChild\n}: TNodeChildrenRendererProps): TChildrenRendererProps {\n const { enableExperimentalMarginCollapsing } = useSharedProps();\n const shouldCollapseChildren =\n enableExperimentalMarginCollapsing &&\n !disableMarginCollapsing &&\n isCollapsible(tnode);\n return {\n propsForChildren,\n disableMarginCollapsing: !shouldCollapseChildren,\n tchildren: tnode.children,\n renderChild\n };\n}\n\n/**\n * A component to render all children of a {@link TNode}.\n */\nfunction TNodeChildrenRenderer(\n props: TNodeChildrenRendererProps\n): ReactElement {\n if (props.tnode.type === 'text') {\n // see https://github.com/DefinitelyTyped/DefinitelyTyped/issues/20544\n return props.tnode.data as unknown as ReactElement;\n }\n // A tnode type will never change. We can safely\n // ignore the non-conditional rule of hooks.\n // eslint-disable-next-line react-hooks/rules-of-hooks\n return renderChildren(useTNodeChildrenProps(props));\n}\n\n/**\n * @ignore\n */\nTNodeChildrenRenderer.defaultProps = tchildrenRendererDefaultProps;\n\nexport default TNodeChildrenRenderer;\n"]} +\ No newline at end of file ++{"version":3,"sources":["TNodeChildrenRenderer.tsx"],"names":["isCollapsible","tnode","type","useTNodeChildrenProps","propsForChildren","disableMarginCollapsing","renderChild","enableExperimentalMarginCollapsing","shouldCollapseChildren","tchildren","children","TNodeChildrenRenderer","props","data"],"mappings":";;;;;;;;AAEA;;AAKA;;;;AAEA,SAASA,aAAT,CAAuBC,KAAvB,EAAqC;AACnC,SAAOA,KAAK,CAACC,IAAN,KAAe,OAAf,IAA0BD,KAAK,CAACC,IAAN,KAAe,UAAhD;AACD;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AACO,SAASC,qBAAT,CAA+B;AACpCF,EAAAA,KADoC;AAEpCG,EAAAA,gBAFoC;AAGpCC,EAAAA,uBAAuB,GAAG,KAHU;AAIpCC,EAAAA;AAJoC,CAA/B,EAKgD;AACrD,QAAM;AAAEC,IAAAA;AAAF,MAAyC,0CAA/C;AACA,QAAMC,sBAAsB,GAC1BD,kCAAkC,IAClC,CAACF,uBADD,IAEAL,aAAa,CAACC,KAAD,CAHf;AAIA,SAAO;AACLG,IAAAA,gBADK;AAELC,IAAAA,uBAAuB,EAAE,CAACG,sBAFrB;AAGLC,IAAAA,SAAS,EAAER,KAAK,CAACS,QAHZ;AAILJ,IAAAA;AAJK,GAAP;AAMD;AAED;AACA;AACA;;;AACA,SAASK,qBAAT,CACEC,KADF,EAEgB;AACd,MAAIA,KAAK,CAACX,KAAN,CAAYC,IAAZ,KAAqB,MAAzB,EAAiC;AAC/B;AACA,WAAOU,KAAK,CAACX,KAAN,CAAYY,IAAnB;AACD,GAJa,CAKd;AACA;AACA;;;AACA,SAAO,6BAAeV,qBAAqB,CAACS,KAAD,CAApC,CAAP;AACD;;eAEcD,qB","sourcesContent":["import { ReactElement } from 'react';\nimport { TNode } from '@native-html/transient-render-engine';\nimport { useSharedProps } from './context/SharedPropsProvider';\nimport {\n TChildrenRendererProps,\n TNodeChildrenRendererProps\n} from './shared-types';\nimport renderChildren from './renderChildren';\n\nfunction isCollapsible(tnode: TNode) {\n return tnode.type === 'block' || tnode.type === 'phrasing';\n}\n\n/**\n * A hook especially useful when one need to tamper with children in a custom\n * renderer. Should be used with {@link TChildrenRenderer}.\n *\n * @example\n * For example, a custom renderer which inserts ads in an article:\n *\n * ```tsx\n * function ArticleRenderer(props) {\n * const { tnode, TDefaultRenderer, ...defaultRendererProps } = props;\n * const tchildrenProps = useTNodeChildrenProps(props);\n * const firstChildrenChunk = tnode.children.slice(0, 2);\n * const secondChildrenChunk = tnode.children.slice(2, 4);\n * const thirdChildrenChunk = tnode.children.slice(4, 5);\n * return (\n * \n * \n * {firstChildrenChunk.length === 2 ? : null}\n * \n * {secondChildrenChunk.length === 2 ? : null}\n * \n * \n * );\n * };\n * ```\n */\nexport function useTNodeChildrenProps({\n tnode,\n propsForChildren,\n disableMarginCollapsing = false,\n renderChild\n}: TNodeChildrenRendererProps): TChildrenRendererProps {\n const { enableExperimentalMarginCollapsing } = useSharedProps();\n const shouldCollapseChildren =\n enableExperimentalMarginCollapsing &&\n !disableMarginCollapsing &&\n isCollapsible(tnode);\n return {\n propsForChildren,\n disableMarginCollapsing: !shouldCollapseChildren,\n tchildren: tnode.children,\n renderChild\n };\n}\n\n/**\n * A component to render all children of a {@link TNode}.\n */\nfunction TNodeChildrenRenderer(\n props: TNodeChildrenRendererProps\n): ReactElement {\n if (props.tnode.type === 'text') {\n // see https://github.com/DefinitelyTyped/DefinitelyTyped/issues/20544\n return props.tnode.data as unknown as ReactElement;\n }\n // A tnode type will never change. We can safely\n // ignore the non-conditional rule of hooks.\n // eslint-disable-next-line react-hooks/rules-of-hooks\n return renderChildren(useTNodeChildrenProps(props));\n}\n\nexport default TNodeChildrenRenderer;\n"]} +\ No newline at end of file +diff --git a/node_modules/react-native-render-html/lib/commonjs/TNodeRenderer.js b/node_modules/react-native-render-html/lib/commonjs/TNodeRenderer.js +index eafc942..e083941 100644 +--- a/node_modules/react-native-render-html/lib/commonjs/TNodeRenderer.js ++++ b/node_modules/react-native-render-html/lib/commonjs/TNodeRenderer.js +@@ -57,7 +57,11 @@ const TNodeRenderer = /*#__PURE__*/(0, _react.memo)(function MemoizedTNodeRender + const sharedProps = (0, _SharedPropsProvider.useSharedProps)(); + const renderRegistry = (0, _RenderRegistryProvider.useRendererRegistry)(); + const TNodeChildrenRenderer = (0, _TChildrenRendererContext.useTNodeChildrenRenderer)(); +- const tnodeProps = { ...props, ++ const tnodeProps = { ++ propsFromParent: { ++ collapsedMarginTop: null ++ }, ++ ...props, + TNodeChildrenRenderer, + sharedProps + }; +@@ -109,13 +113,6 @@ const TNodeRenderer = /*#__PURE__*/(0, _react.memo)(function MemoizedTNodeRender + const renderFn = tnode.type === 'block' || tnode.type === 'document' ? _renderBlockContent.default : _renderTextualContent.default; + return Renderer === null ? renderFn(assembledProps) : /*#__PURE__*/_react.default.createElement(Renderer, assembledProps); + }); +-const defaultProps = { +- propsFromParent: { +- collapsedMarginTop: null +- } +-}; // @ts-expect-error default props must be defined +- +-TNodeRenderer.defaultProps = defaultProps; + var _default = TNodeRenderer; + exports.default = _default; + //# sourceMappingURL=TNodeRenderer.js.map +\ No newline at end of file +diff --git a/node_modules/react-native-render-html/lib/commonjs/TNodeRenderer.js.map b/node_modules/react-native-render-html/lib/commonjs/TNodeRenderer.js.map +index 204b82a..7721e92 100644 +--- a/node_modules/react-native-render-html/lib/commonjs/TNodeRenderer.js.map ++++ b/node_modules/react-native-render-html/lib/commonjs/TNodeRenderer.js.map +@@ -1 +1 @@ +-{"version":3,"sources":["TNodeRenderer.tsx"],"names":["TDefaultBlockRenderer","renderBlockContent","bind","displayName","TDefaultPhrasingRenderer","renderTextualContent","TDefaultTextRenderer","isGhostTNode","tnode","type","data","TNodeRenderer","MemoizedTNodeRenderer","props","sharedProps","renderRegistry","TNodeChildrenRenderer","tnodeProps","renderer","renderEmptyContent","assembledProps","Renderer","InternalTextRenderer","getInternalTextRenderer","tagName","React","createElement","enableExperimentalGhostLinesPrevention","bypassAnonymousTPhrasingNodes","children","length","every","renderFn","defaultProps","propsFromParent","collapsedMarginTop"],"mappings":";;;;;;;AAAA;;AAEA;;AAOA;;AACA;;AACA;;AACA;;AACA;;AACA;;;;;;;;AAIA,MAAMA,qBAA+C,GACnDC,4BAAmBC,IAAnB,CAAwB,IAAxB,CADF;;;AAGAF,qBAAqB,CAACG,WAAtB,GAAoC,uBAApC;;AAEA,MAAMC,wBAAqD,GACzDC,8BAAqBH,IAArB,CAA0B,IAA1B,CADF;;;AAGAE,wBAAwB,CAACD,WAAzB,GAAuC,0BAAvC;;AAEA,MAAMG,oBAA6C,GACjDD,8BAAqBH,IAArB,CAA0B,IAA1B,CADF;;;AAGAI,oBAAoB,CAACH,WAArB,GAAmC,sBAAnC;;AAEA,SAASI,YAAT,CAAsBC,KAAtB,EAAoC;AAClC,SACGA,KAAK,CAACC,IAAN,KAAe,MAAf,KAA0BD,KAAK,CAACE,IAAN,KAAe,EAAf,IAAqBF,KAAK,CAACE,IAAN,KAAe,GAA9D,CAAD,IACAF,KAAK,CAACC,IAAN,KAAe,OAFjB;AAID;AAED;AACA;AACA;;;AACA,MAAME,aAAa,gBAAG,iBAAK,SAASC,qBAAT,CACzBC,KADyB,EAEJ;AACrB,QAAM;AAAEL,IAAAA;AAAF,MAAYK,KAAlB;AACA,QAAMC,WAAW,GAAG,0CAApB;AACA,QAAMC,cAAc,GAAG,kDAAvB;AACA,QAAMC,qBAAqB,GAAG,yDAA9B;AACA,QAAMC,UAAU,GAAG,EACjB,GAAGJ,KADc;AAEjBG,IAAAA,qBAFiB;AAGjBF,IAAAA;AAHiB,GAAnB;AAKA,QAAMI,QAAQ,GACZV,KAAK,CAACC,IAAN,KAAe,OAAf,IAA0BD,KAAK,CAACC,IAAN,KAAe,UAAzC,GACIT,qBADJ,GAEIQ,KAAK,CAACC,IAAN,KAAe,MAAf,GACAH,oBADA,GAEAE,KAAK,CAACC,IAAN,KAAe,UAAf,GACAL,wBADA,GAEAe,2BAPN;AASA,QAAM;AAAEC,IAAAA,cAAF;AAAkBC,IAAAA;AAAlB,MAA+B,sCACnCJ,UADmC,EAEnCC,QAFmC,CAArC;;AAIA,UAAQV,KAAK,CAACC,IAAd;AACE,SAAK,OAAL;AACE,aAAO,iCAAmBW,cAAnB,CAAP;;AACF,SAAK,MAAL;AACE,YAAME,oBAAoB,GAAGP,cAAc,CAACQ,uBAAf,CAC3BV,KAAK,CAACL,KAAN,CAAYgB,OADe,CAA7B;;AAIA,UAAIF,oBAAJ,EAA0B;AACxB,4BAAOG,eAAMC,aAAN,CAAoBJ,oBAApB,EAA0CL,UAA1C,CAAP;AACD,OAPH,CAQE;AACA;AACA;;;AACA,UACEA,UAAU,CAACT,KAAX,CAAiBE,IAAjB,KAA0B,EAA1B,IACAO,UAAU,CAACH,WAAX,CAAuBa,sCAFzB,EAGE;AACA,eAAO,IAAP;AACD;;AACD;;AACF,SAAK,UAAL;AACE;AACA;AACA,UACEV,UAAU,CAACH,WAAX,CAAuBc,6BAAvB,IACAX,UAAU,CAACT,KAAX,CAAiBgB,OAAjB,IAA4B,IAD5B,IAEAP,UAAU,CAACT,KAAX,CAAiBqB,QAAjB,CAA0BC,MAA1B,IAAoC,CAHtC,EAIE;AACA,4BAAOL,eAAMC,aAAN,CAAoBV,qBAApB,EAA2C;AAChDR,UAAAA,KAAK,EAAEK,KAAK,CAACL;AADmC,SAA3C,CAAP;AAGD,OAXH,CAYE;AACA;AACA;;;AACA,UACES,UAAU,CAACH,WAAX,CAAuBa,sCAAvB,IACAV,UAAU,CAACT,KAAX,CAAiBgB,OAAjB,IAA4B,IAD5B,IAEAP,UAAU,CAACT,KAAX,CAAiBqB,QAAjB,CAA0BE,KAA1B,CAAgCxB,YAAhC,CAHF,EAIE;AACA,eAAO,IAAP;AACD;;AACD;AA3CJ;;AA6CA,QAAMyB,QAAQ,GACZxB,KAAK,CAACC,IAAN,KAAe,OAAf,IAA0BD,KAAK,CAACC,IAAN,KAAe,UAAzC,GACIR,2BADJ,GAEII,6BAHN;AAIA,SAAOgB,QAAQ,KAAK,IAAb,GACHW,QAAQ,CAACZ,cAAD,CADL,gBAEHK,eAAMC,aAAN,CAAoBL,QAApB,EAAqCD,cAArC,CAFJ;AAGD,CA7EqB,CAAtB;AA+EA,MAAMa,YAAwE,GAC5E;AACEC,EAAAA,eAAe,EAAE;AACfC,IAAAA,kBAAkB,EAAE;AADL;AADnB,CADF,C,CAOA;;AACAxB,aAAa,CAACsB,YAAd,GAA6BA,YAA7B;eAQetB,a","sourcesContent":["import React, { memo, ReactElement } from 'react';\nimport { TDefaultRenderer, TNodeRendererProps } from './shared-types';\nimport { useSharedProps } from './context/SharedPropsProvider';\nimport {\n TText,\n TBlock,\n TNode,\n TPhrasing\n} from '@native-html/transient-render-engine';\nimport useAssembledCommonProps from './hooks/useAssembledCommonProps';\nimport { useTNodeChildrenRenderer } from './context/TChildrenRendererContext';\nimport renderTextualContent from './renderTextualContent';\nimport { useRendererRegistry } from './context/RenderRegistryProvider';\nimport renderBlockContent from './renderBlockContent';\nimport renderEmptyContent from './renderEmptyContent';\n\nexport type { TNodeRendererProps } from './shared-types';\n\nconst TDefaultBlockRenderer: TDefaultRenderer =\n renderBlockContent.bind(null);\n\nTDefaultBlockRenderer.displayName = 'TDefaultBlockRenderer';\n\nconst TDefaultPhrasingRenderer: TDefaultRenderer =\n renderTextualContent.bind(null);\n\nTDefaultPhrasingRenderer.displayName = 'TDefaultPhrasingRenderer';\n\nconst TDefaultTextRenderer: TDefaultRenderer =\n renderTextualContent.bind(null);\n\nTDefaultTextRenderer.displayName = 'TDefaultTextRenderer';\n\nfunction isGhostTNode(tnode: TNode) {\n return (\n (tnode.type === 'text' && (tnode.data === '' || tnode.data === ' ')) ||\n tnode.type === 'empty'\n );\n}\n\n/**\n * A component to render any {@link TNode}.\n */\nconst TNodeRenderer = memo(function MemoizedTNodeRenderer(\n props: TNodeRendererProps\n): ReactElement | null {\n const { tnode } = props;\n const sharedProps = useSharedProps();\n const renderRegistry = useRendererRegistry();\n const TNodeChildrenRenderer = useTNodeChildrenRenderer();\n const tnodeProps = {\n ...props,\n TNodeChildrenRenderer,\n sharedProps\n };\n const renderer =\n tnode.type === 'block' || tnode.type === 'document'\n ? TDefaultBlockRenderer\n : tnode.type === 'text'\n ? TDefaultTextRenderer\n : tnode.type === 'phrasing'\n ? TDefaultPhrasingRenderer\n : renderEmptyContent;\n\n const { assembledProps, Renderer } = useAssembledCommonProps(\n tnodeProps,\n renderer as any\n );\n switch (tnode.type) {\n case 'empty':\n return renderEmptyContent(assembledProps);\n case 'text':\n const InternalTextRenderer = renderRegistry.getInternalTextRenderer(\n props.tnode.tagName\n );\n\n if (InternalTextRenderer) {\n return React.createElement(InternalTextRenderer, tnodeProps);\n }\n // If ghost line prevention is enabled and the text data is empty, render\n // nothing to avoid React Native painting a 20px height line.\n // See also https://git.io/JErwX\n if (\n tnodeProps.tnode.data === '' &&\n tnodeProps.sharedProps.enableExperimentalGhostLinesPrevention\n ) {\n return null;\n }\n break;\n case 'phrasing':\n // When a TPhrasing node is anonymous and has only one child, its\n // rendering amounts to rendering its only child.\n if (\n tnodeProps.sharedProps.bypassAnonymousTPhrasingNodes &&\n tnodeProps.tnode.tagName == null &&\n tnodeProps.tnode.children.length <= 1\n ) {\n return React.createElement(TNodeChildrenRenderer, {\n tnode: props.tnode\n });\n }\n // If ghost line prevention is enabled and the tnode is an anonymous empty\n // phrasing node, render nothing to avoid React Native painting a 20px\n // height line. See also https://git.io/JErwX\n if (\n tnodeProps.sharedProps.enableExperimentalGhostLinesPrevention &&\n tnodeProps.tnode.tagName == null &&\n tnodeProps.tnode.children.every(isGhostTNode)\n ) {\n return null;\n }\n break;\n }\n const renderFn =\n tnode.type === 'block' || tnode.type === 'document'\n ? renderBlockContent\n : renderTextualContent;\n return Renderer === null\n ? renderFn(assembledProps)\n : React.createElement(Renderer as any, assembledProps);\n});\n\nconst defaultProps: Required, 'propsFromParent'>> =\n {\n propsFromParent: {\n collapsedMarginTop: null\n }\n };\n\n// @ts-expect-error default props must be defined\nTNodeRenderer.defaultProps = defaultProps;\n\nexport {\n TDefaultBlockRenderer,\n TDefaultPhrasingRenderer,\n TDefaultTextRenderer\n};\n\nexport default TNodeRenderer;\n"]} +\ No newline at end of file ++{"version":3,"sources":["TNodeRenderer.tsx"],"names":["TDefaultBlockRenderer","renderBlockContent","bind","displayName","TDefaultPhrasingRenderer","renderTextualContent","TDefaultTextRenderer","isGhostTNode","tnode","type","data","TNodeRenderer","MemoizedTNodeRenderer","props","sharedProps","renderRegistry","TNodeChildrenRenderer","tnodeProps","propsFromParent","collapsedMarginTop","renderer","renderEmptyContent","assembledProps","Renderer","InternalTextRenderer","getInternalTextRenderer","tagName","React","createElement","enableExperimentalGhostLinesPrevention","bypassAnonymousTPhrasingNodes","children","length","every","renderFn"],"mappings":";;;;;;;AAAA;;AAEA;;AAOA;;AACA;;AACA;;AACA;;AACA;;AACA;;;;;;;;AAIA,MAAMA,qBAA+C,GACnDC,4BAAmBC,IAAnB,CAAwB,IAAxB,CADF;;;AAGAF,qBAAqB,CAACG,WAAtB,GAAoC,uBAApC;;AAEA,MAAMC,wBAAqD,GACzDC,8BAAqBH,IAArB,CAA0B,IAA1B,CADF;;;AAGAE,wBAAwB,CAACD,WAAzB,GAAuC,0BAAvC;;AAEA,MAAMG,oBAA6C,GACjDD,8BAAqBH,IAArB,CAA0B,IAA1B,CADF;;;AAGAI,oBAAoB,CAACH,WAArB,GAAmC,sBAAnC;;AAEA,SAASI,YAAT,CAAsBC,KAAtB,EAAoC;AAClC,SACGA,KAAK,CAACC,IAAN,KAAe,MAAf,KAA0BD,KAAK,CAACE,IAAN,KAAe,EAAf,IAAqBF,KAAK,CAACE,IAAN,KAAe,GAA9D,CAAD,IACAF,KAAK,CAACC,IAAN,KAAe,OAFjB;AAID;AAED;AACA;AACA;;;AACA,MAAME,aAAa,gBAAG,iBAAK,SAASC,qBAAT,CACzBC,KADyB,EAEJ;AACrB,QAAM;AAAEL,IAAAA;AAAF,MAAYK,KAAlB;AACA,QAAMC,WAAW,GAAG,0CAApB;AACA,QAAMC,cAAc,GAAG,kDAAvB;AACA,QAAMC,qBAAqB,GAAG,yDAA9B;AACA,QAAMC,UAAU,GAAG;AACjBC,IAAAA,eAAe,EAAE;AAAEC,MAAAA,kBAAkB,EAAE;AAAtB,KADA;AAEjB,OAAGN,KAFc;AAGjBG,IAAAA,qBAHiB;AAIjBF,IAAAA;AAJiB,GAAnB;AAMA,QAAMM,QAAQ,GACZZ,KAAK,CAACC,IAAN,KAAe,OAAf,IAA0BD,KAAK,CAACC,IAAN,KAAe,UAAzC,GACIT,qBADJ,GAEIQ,KAAK,CAACC,IAAN,KAAe,MAAf,GACAH,oBADA,GAEAE,KAAK,CAACC,IAAN,KAAe,UAAf,GACAL,wBADA,GAEAiB,2BAPN;AASA,QAAM;AAAEC,IAAAA,cAAF;AAAkBC,IAAAA;AAAlB,MAA+B,sCACnCN,UADmC,EAEnCG,QAFmC,CAArC;;AAIA,UAAQZ,KAAK,CAACC,IAAd;AACE,SAAK,OAAL;AACE,aAAO,iCAAmBa,cAAnB,CAAP;;AACF,SAAK,MAAL;AACE,YAAME,oBAAoB,GAAGT,cAAc,CAACU,uBAAf,CAC3BZ,KAAK,CAACL,KAAN,CAAYkB,OADe,CAA7B;;AAIA,UAAIF,oBAAJ,EAA0B;AACxB,4BAAOG,eAAMC,aAAN,CAAoBJ,oBAApB,EAA0CP,UAA1C,CAAP;AACD,OAPH,CAQE;AACA;AACA;;;AACA,UACEA,UAAU,CAACT,KAAX,CAAiBE,IAAjB,KAA0B,EAA1B,IACAO,UAAU,CAACH,WAAX,CAAuBe,sCAFzB,EAGE;AACA,eAAO,IAAP;AACD;;AACD;;AACF,SAAK,UAAL;AACE;AACA;AACA,UACEZ,UAAU,CAACH,WAAX,CAAuBgB,6BAAvB,IACAb,UAAU,CAACT,KAAX,CAAiBkB,OAAjB,IAA4B,IAD5B,IAEAT,UAAU,CAACT,KAAX,CAAiBuB,QAAjB,CAA0BC,MAA1B,IAAoC,CAHtC,EAIE;AACA,4BAAOL,eAAMC,aAAN,CAAoBZ,qBAApB,EAA2C;AAChDR,UAAAA,KAAK,EAAEK,KAAK,CAACL;AADmC,SAA3C,CAAP;AAGD,OAXH,CAYE;AACA;AACA;;;AACA,UACES,UAAU,CAACH,WAAX,CAAuBe,sCAAvB,IACAZ,UAAU,CAACT,KAAX,CAAiBkB,OAAjB,IAA4B,IAD5B,IAEAT,UAAU,CAACT,KAAX,CAAiBuB,QAAjB,CAA0BE,KAA1B,CAAgC1B,YAAhC,CAHF,EAIE;AACA,eAAO,IAAP;AACD;;AACD;AA3CJ;;AA6CA,QAAM2B,QAAQ,GACZ1B,KAAK,CAACC,IAAN,KAAe,OAAf,IAA0BD,KAAK,CAACC,IAAN,KAAe,UAAzC,GACIR,2BADJ,GAEII,6BAHN;AAIA,SAAOkB,QAAQ,KAAK,IAAb,GACHW,QAAQ,CAACZ,cAAD,CADL,gBAEHK,eAAMC,aAAN,CAAoBL,QAApB,EAAqCD,cAArC,CAFJ;AAGD,CA9EqB,CAAtB;eAsFeX,a","sourcesContent":["import React, { memo, ReactElement } from 'react';\nimport { TDefaultRenderer, TNodeRendererProps } from './shared-types';\nimport { useSharedProps } from './context/SharedPropsProvider';\nimport {\n TText,\n TBlock,\n TNode,\n TPhrasing\n} from '@native-html/transient-render-engine';\nimport useAssembledCommonProps from './hooks/useAssembledCommonProps';\nimport { useTNodeChildrenRenderer } from './context/TChildrenRendererContext';\nimport renderTextualContent from './renderTextualContent';\nimport { useRendererRegistry } from './context/RenderRegistryProvider';\nimport renderBlockContent from './renderBlockContent';\nimport renderEmptyContent from './renderEmptyContent';\n\nexport type { TNodeRendererProps } from './shared-types';\n\nconst TDefaultBlockRenderer: TDefaultRenderer =\n renderBlockContent.bind(null);\n\nTDefaultBlockRenderer.displayName = 'TDefaultBlockRenderer';\n\nconst TDefaultPhrasingRenderer: TDefaultRenderer =\n renderTextualContent.bind(null);\n\nTDefaultPhrasingRenderer.displayName = 'TDefaultPhrasingRenderer';\n\nconst TDefaultTextRenderer: TDefaultRenderer =\n renderTextualContent.bind(null);\n\nTDefaultTextRenderer.displayName = 'TDefaultTextRenderer';\n\nfunction isGhostTNode(tnode: TNode) {\n return (\n (tnode.type === 'text' && (tnode.data === '' || tnode.data === ' ')) ||\n tnode.type === 'empty'\n );\n}\n\n/**\n * A component to render any {@link TNode}.\n */\nconst TNodeRenderer = memo(function MemoizedTNodeRenderer(\n props: TNodeRendererProps\n): ReactElement | null {\n const { tnode } = props;\n const sharedProps = useSharedProps();\n const renderRegistry = useRendererRegistry();\n const TNodeChildrenRenderer = useTNodeChildrenRenderer();\n const tnodeProps = {\n propsFromParent: { collapsedMarginTop: null },\n ...props,\n TNodeChildrenRenderer,\n sharedProps\n };\n const renderer =\n tnode.type === 'block' || tnode.type === 'document'\n ? TDefaultBlockRenderer\n : tnode.type === 'text'\n ? TDefaultTextRenderer\n : tnode.type === 'phrasing'\n ? TDefaultPhrasingRenderer\n : renderEmptyContent;\n\n const { assembledProps, Renderer } = useAssembledCommonProps(\n tnodeProps,\n renderer as any\n );\n switch (tnode.type) {\n case 'empty':\n return renderEmptyContent(assembledProps);\n case 'text':\n const InternalTextRenderer = renderRegistry.getInternalTextRenderer(\n props.tnode.tagName\n );\n\n if (InternalTextRenderer) {\n return React.createElement(InternalTextRenderer, tnodeProps);\n }\n // If ghost line prevention is enabled and the text data is empty, render\n // nothing to avoid React Native painting a 20px height line.\n // See also https://git.io/JErwX\n if (\n tnodeProps.tnode.data === '' &&\n tnodeProps.sharedProps.enableExperimentalGhostLinesPrevention\n ) {\n return null;\n }\n break;\n case 'phrasing':\n // When a TPhrasing node is anonymous and has only one child, its\n // rendering amounts to rendering its only child.\n if (\n tnodeProps.sharedProps.bypassAnonymousTPhrasingNodes &&\n tnodeProps.tnode.tagName == null &&\n tnodeProps.tnode.children.length <= 1\n ) {\n return React.createElement(TNodeChildrenRenderer, {\n tnode: props.tnode\n });\n }\n // If ghost line prevention is enabled and the tnode is an anonymous empty\n // phrasing node, render nothing to avoid React Native painting a 20px\n // height line. See also https://git.io/JErwX\n if (\n tnodeProps.sharedProps.enableExperimentalGhostLinesPrevention &&\n tnodeProps.tnode.tagName == null &&\n tnodeProps.tnode.children.every(isGhostTNode)\n ) {\n return null;\n }\n break;\n }\n const renderFn =\n tnode.type === 'block' || tnode.type === 'document'\n ? renderBlockContent\n : renderTextualContent;\n return Renderer === null\n ? renderFn(assembledProps)\n : React.createElement(Renderer as any, assembledProps);\n});\n\nexport {\n TDefaultBlockRenderer,\n TDefaultPhrasingRenderer,\n TDefaultTextRenderer\n};\n\nexport default TNodeRenderer;\n"]} +\ No newline at end of file +diff --git a/node_modules/react-native-render-html/lib/commonjs/TRenderEngineProvider.js b/node_modules/react-native-render-html/lib/commonjs/TRenderEngineProvider.js +index 3a700b6..89a4dd4 100644 +--- a/node_modules/react-native-render-html/lib/commonjs/TRenderEngineProvider.js ++++ b/node_modules/react-native-render-html/lib/commonjs/TRenderEngineProvider.js +@@ -5,89 +5,16 @@ Object.defineProperty(exports, "__esModule", { + }); + exports.useAmbientTRenderEngine = useAmbientTRenderEngine; + exports.default = TRenderEngineProvider; +-exports.defaultTRenderEngineProviderProps = exports.defaultFallbackFonts = exports.tRenderEngineProviderPropTypes = void 0; + + var _react = _interopRequireDefault(require("react")); + +-var _reactNative = require("react-native"); +- +-var _propTypes = _interopRequireDefault(require("prop-types")); +- + var _useTRenderEngine = _interopRequireDefault(require("./hooks/useTRenderEngine")); + +-var _defaultSystemFonts = _interopRequireDefault(require("./defaultSystemFonts")); +- + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + const defaultTRenderEngine = {}; + + const TRenderEngineContext = /*#__PURE__*/_react.default.createContext(defaultTRenderEngine); +- +-const tRenderEngineProviderPropTypes = { +- customHTMLElementModels: _propTypes.default.object.isRequired, +- enableCSSInlineProcessing: _propTypes.default.bool, +- enableUserAgentStyles: _propTypes.default.bool, +- idsStyles: _propTypes.default.object, +- ignoredDomTags: _propTypes.default.array, +- ignoreDomNode: _propTypes.default.func, +- domVisitors: _propTypes.default.object, +- ignoredStyles: _propTypes.default.array.isRequired, +- allowedStyles: _propTypes.default.array, +- htmlParserOptions: _propTypes.default.object, +- tagsStyles: _propTypes.default.object, +- classesStyles: _propTypes.default.object, +- emSize: _propTypes.default.number.isRequired, +- baseStyle: _propTypes.default.object, +- systemFonts: _propTypes.default.arrayOf(_propTypes.default.string), +- fallbackFonts: _propTypes.default.shape({ +- serif: _propTypes.default.string, +- 'sans-serif': _propTypes.default.string, +- monospace: _propTypes.default.string +- }), +- setMarkersForTNode: _propTypes.default.func, +- dangerouslyDisableHoisting: _propTypes.default.bool, +- dangerouslyDisableWhitespaceCollapsing: _propTypes.default.bool, +- selectDomRoot: _propTypes.default.func +-}; +-/** +- * Default fallback font for special keys such as 'sans-serif', 'monospace', +- * 'serif', based on current platform. +- */ +- +-exports.tRenderEngineProviderPropTypes = tRenderEngineProviderPropTypes; +-const defaultFallbackFonts = { +- 'sans-serif': _reactNative.Platform.select({ +- ios: 'system', +- default: 'sans-serif' +- }), +- monospace: _reactNative.Platform.select({ +- ios: 'Menlo', +- default: 'monospace' +- }), +- serif: _reactNative.Platform.select({ +- ios: 'Times New Roman', +- default: 'serif' +- }) +-}; +-exports.defaultFallbackFonts = defaultFallbackFonts; +-const defaultTRenderEngineProviderProps = { +- htmlParserOptions: { +- decodeEntities: true +- }, +- emSize: 14, +- ignoredDomTags: [], +- ignoredStyles: [], +- baseStyle: { +- fontSize: 14 +- }, +- tagsStyles: {}, +- classesStyles: {}, +- enableUserAgentStyles: true, +- enableCSSInlineProcessing: true, +- customHTMLElementModels: {}, +- fallbackFonts: defaultFallbackFonts, +- systemFonts: _defaultSystemFonts.default +-}; + /** + * Use the ambient transient render engine. + * +@@ -96,7 +23,6 @@ const defaultTRenderEngineProviderProps = { + * @public + */ + +-exports.defaultTRenderEngineProviderProps = defaultTRenderEngineProviderProps; + + function useAmbientTRenderEngine() { + const engine = _react.default.useContext(TRenderEngineContext); +@@ -126,15 +52,4 @@ function TRenderEngineProvider({ + value: engine + }, children); + } +-/** +- * @ignore +- */ +- +- +-TRenderEngineProvider.defaultProps = defaultTRenderEngineProviderProps; +-/** +- * @ignore +- */ +- +-TRenderEngineProvider.propTypes = tRenderEngineProviderPropTypes; + //# sourceMappingURL=TRenderEngineProvider.js.map +\ No newline at end of file +diff --git a/node_modules/react-native-render-html/lib/commonjs/TRenderEngineProvider.js.map b/node_modules/react-native-render-html/lib/commonjs/TRenderEngineProvider.js.map +index cf76758..c21b058 100644 +--- a/node_modules/react-native-render-html/lib/commonjs/TRenderEngineProvider.js.map ++++ b/node_modules/react-native-render-html/lib/commonjs/TRenderEngineProvider.js.map +@@ -1 +1 @@ +-{"version":3,"sources":["TRenderEngineProvider.tsx"],"names":["defaultTRenderEngine","TRenderEngineContext","React","createContext","tRenderEngineProviderPropTypes","customHTMLElementModels","PropTypes","object","isRequired","enableCSSInlineProcessing","bool","enableUserAgentStyles","idsStyles","ignoredDomTags","array","ignoreDomNode","func","domVisitors","ignoredStyles","allowedStyles","htmlParserOptions","tagsStyles","classesStyles","emSize","number","baseStyle","systemFonts","arrayOf","string","fallbackFonts","shape","serif","monospace","setMarkersForTNode","dangerouslyDisableHoisting","dangerouslyDisableWhitespaceCollapsing","selectDomRoot","defaultFallbackFonts","Platform","select","ios","default","defaultTRenderEngineProviderProps","decodeEntities","fontSize","defaultSystemFonts","useAmbientTRenderEngine","engine","useContext","__DEV__","console","error","TRenderEngineProvider","children","config","defaultProps","propTypes"],"mappings":";;;;;;;;;AACA;;AACA;;AACA;;AACA;;AAEA;;;;AAEA,MAAMA,oBAAoB,GAAG,EAA7B;;AAEA,MAAMC,oBAAoB,gBACxBC,eAAMC,aAAN,CAAmCH,oBAAnC,CADF;;AAGO,MAAMI,8BAGZ,GAAG;AACFC,EAAAA,uBAAuB,EAAEC,mBAAUC,MAAV,CAAiBC,UADxC;AAEFC,EAAAA,yBAAyB,EAAEH,mBAAUI,IAFnC;AAGFC,EAAAA,qBAAqB,EAAEL,mBAAUI,IAH/B;AAIFE,EAAAA,SAAS,EAAEN,mBAAUC,MAJnB;AAKFM,EAAAA,cAAc,EAAEP,mBAAUQ,KALxB;AAMFC,EAAAA,aAAa,EAAET,mBAAUU,IANvB;AAOFC,EAAAA,WAAW,EAAEX,mBAAUC,MAPrB;AAQFW,EAAAA,aAAa,EAAEZ,mBAAUQ,KAAV,CAAgBN,UAR7B;AASFW,EAAAA,aAAa,EAAEb,mBAAUQ,KATvB;AAUFM,EAAAA,iBAAiB,EAAEd,mBAAUC,MAV3B;AAWFc,EAAAA,UAAU,EAAEf,mBAAUC,MAXpB;AAYFe,EAAAA,aAAa,EAAEhB,mBAAUC,MAZvB;AAaFgB,EAAAA,MAAM,EAAEjB,mBAAUkB,MAAV,CAAiBhB,UAbvB;AAcFiB,EAAAA,SAAS,EAAEnB,mBAAUC,MAdnB;AAeFmB,EAAAA,WAAW,EAAEpB,mBAAUqB,OAAV,CAAkBrB,mBAAUsB,MAA5B,CAfX;AAgBFC,EAAAA,aAAa,EAAEvB,mBAAUwB,KAAV,CAAgB;AAC7BC,IAAAA,KAAK,EAAEzB,mBAAUsB,MADY;AAE7B,kBAActB,mBAAUsB,MAFK;AAG7BI,IAAAA,SAAS,EAAE1B,mBAAUsB;AAHQ,GAAhB,CAhBb;AAqBFK,EAAAA,kBAAkB,EAAE3B,mBAAUU,IArB5B;AAsBFkB,EAAAA,0BAA0B,EAAE5B,mBAAUI,IAtBpC;AAuBFyB,EAAAA,sCAAsC,EAAE7B,mBAAUI,IAvBhD;AAwBF0B,EAAAA,aAAa,EAAE9B,mBAAUU;AAxBvB,CAHG;AA8BP;AACA;AACA;AACA;;;AACO,MAAMqB,oBAAoB,GAAG;AAClC,gBAAcC,sBAASC,MAAT,CAAgB;AAAEC,IAAAA,GAAG,EAAE,QAAP;AAAiBC,IAAAA,OAAO,EAAE;AAA1B,GAAhB,CADoB;AAElCT,EAAAA,SAAS,EAAEM,sBAASC,MAAT,CAAgB;AAAEC,IAAAA,GAAG,EAAE,OAAP;AAAgBC,IAAAA,OAAO,EAAE;AAAzB,GAAhB,CAFuB;AAGlCV,EAAAA,KAAK,EAAEO,sBAASC,MAAT,CAAgB;AAAEC,IAAAA,GAAG,EAAE,iBAAP;AAA0BC,IAAAA,OAAO,EAAE;AAAnC,GAAhB;AAH2B,CAA7B;;AAMA,MAAMC,iCAAsD,GAAG;AACpEtB,EAAAA,iBAAiB,EAAE;AACjBuB,IAAAA,cAAc,EAAE;AADC,GADiD;AAIpEpB,EAAAA,MAAM,EAAE,EAJ4D;AAKpEV,EAAAA,cAAc,EAAE,EALoD;AAMpEK,EAAAA,aAAa,EAAE,EANqD;AAOpEO,EAAAA,SAAS,EAAE;AAAEmB,IAAAA,QAAQ,EAAE;AAAZ,GAPyD;AAQpEvB,EAAAA,UAAU,EAAE,EARwD;AASpEC,EAAAA,aAAa,EAAE,EATqD;AAUpEX,EAAAA,qBAAqB,EAAE,IAV6C;AAWpEF,EAAAA,yBAAyB,EAAE,IAXyC;AAYpEJ,EAAAA,uBAAuB,EAAE,EAZ2C;AAapEwB,EAAAA,aAAa,EAAEQ,oBAbqD;AAcpEX,EAAAA,WAAW,EAAEmB;AAduD,CAA/D;AAiBP;AACA;AACA;AACA;AACA;AACA;AACA;;;;AACO,SAASC,uBAAT,GAAmC;AACxC,QAAMC,MAAM,GAAG7C,eAAM8C,UAAN,CAAiB/C,oBAAjB,CAAf;;AACA,MACE,OAAOgD,OAAP,KAAmB,SAAnB,IACAA,OADA,IAEAF,MAAM,KAAK/C,oBAHb,EAIE;AACAkD,IAAAA,OAAO,CAACC,KAAR,CAAc,sDAAd;AACD;;AACD,SAAOJ,MAAP;AACD;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AACe,SAASK,qBAAT,CAA+B;AAC5CC,EAAAA,QAD4C;AAE5C,KAAGC;AAFyC,CAA/B,EAG0C;AACvD,QAAMP,MAAM,GAAG,+BAAiBO,MAAjB,CAAf;AACA,sBACE,6BAAC,oBAAD,CAAsB,QAAtB;AAA+B,IAAA,KAAK,EAAEP;AAAtC,KACGM,QADH,CADF;AAKD;AAED;AACA;AACA;;;AACAD,qBAAqB,CAACG,YAAtB,GAAqCb,iCAArC;AAEA;AACA;AACA;;AACAU,qBAAqB,CAACI,SAAtB,GAAkCpD,8BAAlC","sourcesContent":["import TRenderEngine from '@native-html/transient-render-engine';\nimport React, { PropsWithChildren, ReactElement } from 'react';\nimport { Platform } from 'react-native';\nimport PropTypes from 'prop-types';\nimport useTRenderEngine from './hooks/useTRenderEngine';\nimport { TRenderEngineConfig } from './shared-types';\nimport defaultSystemFonts from './defaultSystemFonts';\n\nconst defaultTRenderEngine = {} as any;\n\nconst TRenderEngineContext =\n React.createContext(defaultTRenderEngine);\n\nexport const tRenderEngineProviderPropTypes: Record<\n keyof TRenderEngineConfig,\n any\n> = {\n customHTMLElementModels: PropTypes.object.isRequired,\n enableCSSInlineProcessing: PropTypes.bool,\n enableUserAgentStyles: PropTypes.bool,\n idsStyles: PropTypes.object,\n ignoredDomTags: PropTypes.array,\n ignoreDomNode: PropTypes.func,\n domVisitors: PropTypes.object,\n ignoredStyles: PropTypes.array.isRequired,\n allowedStyles: PropTypes.array,\n htmlParserOptions: PropTypes.object,\n tagsStyles: PropTypes.object,\n classesStyles: PropTypes.object,\n emSize: PropTypes.number.isRequired,\n baseStyle: PropTypes.object,\n systemFonts: PropTypes.arrayOf(PropTypes.string),\n fallbackFonts: PropTypes.shape({\n serif: PropTypes.string,\n 'sans-serif': PropTypes.string,\n monospace: PropTypes.string\n }),\n setMarkersForTNode: PropTypes.func,\n dangerouslyDisableHoisting: PropTypes.bool,\n dangerouslyDisableWhitespaceCollapsing: PropTypes.bool,\n selectDomRoot: PropTypes.func\n};\n\n/**\n * Default fallback font for special keys such as 'sans-serif', 'monospace',\n * 'serif', based on current platform.\n */\nexport const defaultFallbackFonts = {\n 'sans-serif': Platform.select({ ios: 'system', default: 'sans-serif' }),\n monospace: Platform.select({ ios: 'Menlo', default: 'monospace' }),\n serif: Platform.select({ ios: 'Times New Roman', default: 'serif' })\n};\n\nexport const defaultTRenderEngineProviderProps: TRenderEngineConfig = {\n htmlParserOptions: {\n decodeEntities: true\n },\n emSize: 14,\n ignoredDomTags: [],\n ignoredStyles: [],\n baseStyle: { fontSize: 14 },\n tagsStyles: {},\n classesStyles: {},\n enableUserAgentStyles: true,\n enableCSSInlineProcessing: true,\n customHTMLElementModels: {},\n fallbackFonts: defaultFallbackFonts,\n systemFonts: defaultSystemFonts\n};\n\n/**\n * Use the ambient transient render engine.\n *\n * @returns The ambient transient render engine.\n *\n * @public\n */\nexport function useAmbientTRenderEngine() {\n const engine = React.useContext(TRenderEngineContext);\n if (\n typeof __DEV__ === 'boolean' &&\n __DEV__ &&\n engine === defaultTRenderEngine\n ) {\n console.error('TRenderEngineProvider is missing in the render tree.');\n }\n return engine;\n}\n\n/**\n * A react component to share a {@link TRenderEngine} instance across different\n * rendered contents via {@link RenderHTMLSource}. This can significantly enhance\n * performance in applications with potentially dozens or hundreds of distinct\n * rendered snippets such as chat apps.\n *\n * @param props - Pass engine config here.\n */\nexport default function TRenderEngineProvider({\n children,\n ...config\n}: PropsWithChildren): ReactElement {\n const engine = useTRenderEngine(config);\n return (\n \n {children}\n \n );\n}\n\n/**\n * @ignore\n */\nTRenderEngineProvider.defaultProps = defaultTRenderEngineProviderProps;\n\n/**\n * @ignore\n */\nTRenderEngineProvider.propTypes = tRenderEngineProviderPropTypes;\n"]} +\ No newline at end of file ++{"version":3,"sources":["TRenderEngineProvider.tsx"],"names":["defaultTRenderEngine","TRenderEngineContext","React","createContext","useAmbientTRenderEngine","engine","useContext","__DEV__","console","error","TRenderEngineProvider","children","config"],"mappings":";;;;;;;;AACA;;AACA;;;;AAGA,MAAMA,oBAAoB,GAAG,EAA7B;;AAEA,MAAMC,oBAAoB,gBACxBC,eAAMC,aAAN,CAAmCH,oBAAnC,CADF;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;;;AACO,SAASI,uBAAT,GAAmC;AACxC,QAAMC,MAAM,GAAGH,eAAMI,UAAN,CAAiBL,oBAAjB,CAAf;;AACA,MACE,OAAOM,OAAP,KAAmB,SAAnB,IACAA,OADA,IAEAF,MAAM,KAAKL,oBAHb,EAIE;AACAQ,IAAAA,OAAO,CAACC,KAAR,CAAc,sDAAd;AACD;;AACD,SAAOJ,MAAP;AACD;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AACe,SAASK,qBAAT,CAA+B;AAC5CC,EAAAA,QAD4C;AAE5C,KAAGC;AAFyC,CAA/B,EAG0C;AACvD,QAAMP,MAAM,GAAG,+BAAiBO,MAAjB,CAAf;AACA,sBACE,6BAAC,oBAAD,CAAsB,QAAtB;AAA+B,IAAA,KAAK,EAAEP;AAAtC,KACGM,QADH,CADF;AAKD","sourcesContent":["import TRenderEngine from '@native-html/transient-render-engine';\nimport React, { PropsWithChildren, ReactElement } from 'react';\nimport useTRenderEngine from './hooks/useTRenderEngine';\nimport { TRenderEngineConfig } from './shared-types';\n\nconst defaultTRenderEngine = {} as any;\n\nconst TRenderEngineContext =\n React.createContext(defaultTRenderEngine);\n\n/**\n * Use the ambient transient render engine.\n *\n * @returns The ambient transient render engine.\n *\n * @public\n */\nexport function useAmbientTRenderEngine() {\n const engine = React.useContext(TRenderEngineContext);\n if (\n typeof __DEV__ === 'boolean' &&\n __DEV__ &&\n engine === defaultTRenderEngine\n ) {\n console.error('TRenderEngineProvider is missing in the render tree.');\n }\n return engine;\n}\n\n/**\n * A react component to share a {@link TRenderEngine} instance across different\n * rendered contents via {@link RenderHTMLSource}. This can significantly enhance\n * performance in applications with potentially dozens or hundreds of distinct\n * rendered snippets such as chat apps.\n *\n * @param props - Pass engine config here.\n */\nexport default function TRenderEngineProvider({\n children,\n ...config\n}: PropsWithChildren): ReactElement {\n const engine = useTRenderEngine(config);\n return (\n \n {children}\n \n );\n}\n"]} +\ No newline at end of file +diff --git a/node_modules/react-native-render-html/lib/commonjs/elements/IMGElement.js b/node_modules/react-native-render-html/lib/commonjs/elements/IMGElement.js +index 1be151a..3a076d4 100644 +--- a/node_modules/react-native-render-html/lib/commonjs/elements/IMGElement.js ++++ b/node_modules/react-native-render-html/lib/commonjs/elements/IMGElement.js +@@ -7,8 +7,6 @@ exports.default = void 0; + + var _react = _interopRequireDefault(require("react")); + +-var _propTypes = _interopRequireDefault(require("prop-types")); +- + var _useIMGElementState = _interopRequireDefault(require("./useIMGElementState")); + + var _IMGElementContentSuccess = _interopRequireDefault(require("./IMGElementContentSuccess")); +@@ -19,15 +17,10 @@ var _IMGElementContentLoading = _interopRequireDefault(require("./IMGElementCont + + var _IMGElementContentError = _interopRequireDefault(require("./IMGElementContentError")); + +-var _defaultInitialImageDimensions = _interopRequireDefault(require("./defaultInitialImageDimensions")); +- + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } + +-function identity(arg) { +- return arg; +-} + /** + * A component to render images based on an internal loading state. + * +@@ -37,8 +30,6 @@ function identity(arg) { + * {@link IMGElementContentSuccess}, {@link IMGElementContentLoading} + * and {@link IMGElementContentError} for customization. + */ +- +- + function IMGElement(props) { + const state = (0, _useIMGElementState.default)(props); + let content; +@@ -59,43 +50,6 @@ function IMGElement(props) { + }), content); + } + +-const imgDimensionsType = _propTypes.default.shape({ +- width: _propTypes.default.number, +- height: _propTypes.default.number +-}); +- +-const propTypes = { +- source: _propTypes.default.object.isRequired, +- alt: _propTypes.default.string, +- altColor: _propTypes.default.string, +- height: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.number]), +- width: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.number]), +- style: _propTypes.default.oneOfType([_propTypes.default.object, _propTypes.default.array]), +- computeMaxWidth: _propTypes.default.func.isRequired, +- contentWidth: _propTypes.default.number, +- enableExperimentalPercentWidth: _propTypes.default.bool, +- initialDimensions: imgDimensionsType, +- onPress: _propTypes.default.func, +- testID: _propTypes.default.string, +- objectFit: _propTypes.default.string, +- cachedNaturalDimensions: imgDimensionsType, +- containerProps: _propTypes.default.object +-}; +-/** +- * @ignore +- */ +- +-IMGElement.propTypes = propTypes; +-/** +- * @ignore +- */ +- +-IMGElement.defaultProps = { +- enableExperimentalPercentWidth: false, +- computeMaxWidth: identity, +- imagesInitialDimensions: _defaultInitialImageDimensions.default, +- style: {} +-}; + var _default = IMGElement; + exports.default = _default; + //# sourceMappingURL=IMGElement.js.map +\ No newline at end of file +diff --git a/node_modules/react-native-render-html/lib/commonjs/elements/IMGElement.js.map b/node_modules/react-native-render-html/lib/commonjs/elements/IMGElement.js.map +index 1e265b3..24ec0d0 100644 +--- a/node_modules/react-native-render-html/lib/commonjs/elements/IMGElement.js.map ++++ b/node_modules/react-native-render-html/lib/commonjs/elements/IMGElement.js.map +@@ -1 +1 @@ +-{"version":3,"sources":["IMGElement.tsx"],"names":["identity","arg","IMGElement","props","state","content","type","React","createElement","IMGElementContentSuccess","IMGElementContentLoading","IMGElementContentError","testID","containerProps","onPress","containerStyle","imgDimensionsType","PropTypes","shape","width","number","height","propTypes","source","object","isRequired","alt","string","altColor","oneOfType","style","array","computeMaxWidth","func","contentWidth","enableExperimentalPercentWidth","bool","initialDimensions","objectFit","cachedNaturalDimensions","defaultProps","imagesInitialDimensions","defaultImageInitialDimensions"],"mappings":";;;;;;;AAAA;;AACA;;AACA;;AACA;;AACA;;AACA;;AACA;;AAEA;;;;;;AAIA,SAASA,QAAT,CAAkBC,GAAlB,EAA4B;AAC1B,SAAOA,GAAP;AACD;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AACA,SAASC,UAAT,CAAoBC,KAApB,EAA0D;AACxD,QAAMC,KAAK,GAAG,iCAAmBD,KAAnB,CAAd;AACA,MAAIE,OAAJ;;AACA,MAAID,KAAK,CAACE,IAAN,KAAe,SAAnB,EAA8B;AAC5BD,IAAAA,OAAO,gBAAGE,eAAMC,aAAN,CAAoBC,iCAApB,EAA8CL,KAA9C,CAAV;AACD,GAFD,MAEO,IAAIA,KAAK,CAACE,IAAN,KAAe,SAAnB,EAA8B;AACnCD,IAAAA,OAAO,gBAAGE,eAAMC,aAAN,CAAoBE,iCAApB,EAA8CN,KAA9C,CAAV;AACD,GAFM,MAEA;AACLC,IAAAA,OAAO,gBAAGE,eAAMC,aAAN,CAAoBG,+BAApB,EAA4CP,KAA5C,CAAV;AACD;;AACD,sBACE,6BAAC,4BAAD;AACE,IAAA,MAAM,EAAED,KAAK,CAACS;AADhB,KAEMT,KAAK,CAACU,cAFZ;AAGE,IAAA,OAAO,EAAEV,KAAK,CAACW,OAHjB;AAIE,IAAA,KAAK,EAAEV,KAAK,CAACW;AAJf,MAKGV,OALH,CADF;AASD;;AAED,MAAMW,iBAAiB,GAAGC,mBAAUC,KAAV,CAAgB;AACxCC,EAAAA,KAAK,EAAEF,mBAAUG,MADuB;AAExCC,EAAAA,MAAM,EAAEJ,mBAAUG;AAFsB,CAAhB,CAA1B;;AAKA,MAAME,SAA6C,GAAG;AACpDC,EAAAA,MAAM,EAAEN,mBAAUO,MAAV,CAAiBC,UAD2B;AAEpDC,EAAAA,GAAG,EAAET,mBAAUU,MAFqC;AAGpDC,EAAAA,QAAQ,EAAEX,mBAAUU,MAHgC;AAIpDN,EAAAA,MAAM,EAAEJ,mBAAUY,SAAV,CAAoB,CAACZ,mBAAUU,MAAX,EAAmBV,mBAAUG,MAA7B,CAApB,CAJ4C;AAKpDD,EAAAA,KAAK,EAAEF,mBAAUY,SAAV,CAAoB,CAACZ,mBAAUU,MAAX,EAAmBV,mBAAUG,MAA7B,CAApB,CAL6C;AAMpDU,EAAAA,KAAK,EAAEb,mBAAUY,SAAV,CAAoB,CAACZ,mBAAUO,MAAX,EAAmBP,mBAAUc,KAA7B,CAApB,CAN6C;AAOpDC,EAAAA,eAAe,EAAEf,mBAAUgB,IAAV,CAAeR,UAPoB;AAQpDS,EAAAA,YAAY,EAAEjB,mBAAUG,MAR4B;AASpDe,EAAAA,8BAA8B,EAAElB,mBAAUmB,IATU;AAUpDC,EAAAA,iBAAiB,EAAErB,iBAViC;AAWpDF,EAAAA,OAAO,EAAEG,mBAAUgB,IAXiC;AAYpDrB,EAAAA,MAAM,EAAEK,mBAAUU,MAZkC;AAapDW,EAAAA,SAAS,EAAErB,mBAAUU,MAb+B;AAcpDY,EAAAA,uBAAuB,EAAEvB,iBAd2B;AAepDH,EAAAA,cAAc,EAAEI,mBAAUO;AAf0B,CAAtD;AAkBA;AACA;AACA;;AACAtB,UAAU,CAACoB,SAAX,GAAuBA,SAAvB;AAEA;AACA;AACA;;AACApB,UAAU,CAACsC,YAAX,GAA0B;AACxBL,EAAAA,8BAA8B,EAAE,KADR;AAExBH,EAAAA,eAAe,EAAEhC,QAFO;AAGxByC,EAAAA,uBAAuB,EAAEC,sCAHD;AAIxBZ,EAAAA,KAAK,EAAE;AAJiB,CAA1B;eAOe5B,U","sourcesContent":["import React, { ReactElement, ReactNode } from 'react';\nimport PropTypes from 'prop-types';\nimport useIMGElementState from './useIMGElementState';\nimport IMGElementContentSuccess from './IMGElementContentSuccess';\nimport IMGElementContainer from './IMGElementContainer';\nimport IMGElementContentLoading from './IMGElementContentLoading';\nimport IMGElementContentError from './IMGElementContentError';\nimport type { IMGElementProps } from './img-types';\nimport defaultImageInitialDimensions from './defaultInitialImageDimensions';\n\nexport type { IMGElementProps } from './img-types';\n\nfunction identity(arg: any) {\n return arg;\n}\n\n/**\n * A component to render images based on an internal loading state.\n *\n * @remarks This component will attempt to draw a box of paint dimensions\n * before retrieving the physical dimensions of the image to avoid layout\n * shifts. See also {@link useIMGElementState}, {@link IMGElementContainer},\n * {@link IMGElementContentSuccess}, {@link IMGElementContentLoading}\n * and {@link IMGElementContentError} for customization.\n */\nfunction IMGElement(props: IMGElementProps): ReactElement {\n const state = useIMGElementState(props);\n let content: ReactNode;\n if (state.type === 'success') {\n content = React.createElement(IMGElementContentSuccess, state);\n } else if (state.type === 'loading') {\n content = React.createElement(IMGElementContentLoading, state);\n } else {\n content = React.createElement(IMGElementContentError, state);\n }\n return (\n \n {content}\n \n );\n}\n\nconst imgDimensionsType = PropTypes.shape({\n width: PropTypes.number,\n height: PropTypes.number\n});\n\nconst propTypes: Record = {\n source: PropTypes.object.isRequired,\n alt: PropTypes.string,\n altColor: PropTypes.string,\n height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),\n width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),\n style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),\n computeMaxWidth: PropTypes.func.isRequired,\n contentWidth: PropTypes.number,\n enableExperimentalPercentWidth: PropTypes.bool,\n initialDimensions: imgDimensionsType,\n onPress: PropTypes.func,\n testID: PropTypes.string,\n objectFit: PropTypes.string,\n cachedNaturalDimensions: imgDimensionsType,\n containerProps: PropTypes.object\n};\n\n/**\n * @ignore\n */\nIMGElement.propTypes = propTypes;\n\n/**\n * @ignore\n */\nIMGElement.defaultProps = {\n enableExperimentalPercentWidth: false,\n computeMaxWidth: identity,\n imagesInitialDimensions: defaultImageInitialDimensions,\n style: {}\n};\n\nexport default IMGElement;\n"]} +\ No newline at end of file ++{"version":3,"sources":["IMGElement.tsx"],"names":["IMGElement","props","state","content","type","React","createElement","IMGElementContentSuccess","IMGElementContentLoading","IMGElementContentError","testID","containerProps","onPress","containerStyle"],"mappings":";;;;;;;AAAA;;AACA;;AACA;;AACA;;AACA;;AACA;;;;;;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASA,UAAT,CAAoBC,KAApB,EAA0D;AACxD,QAAMC,KAAK,GAAG,iCAAmBD,KAAnB,CAAd;AACA,MAAIE,OAAJ;;AACA,MAAID,KAAK,CAACE,IAAN,KAAe,SAAnB,EAA8B;AAC5BD,IAAAA,OAAO,gBAAGE,eAAMC,aAAN,CAAoBC,iCAApB,EAA8CL,KAA9C,CAAV;AACD,GAFD,MAEO,IAAIA,KAAK,CAACE,IAAN,KAAe,SAAnB,EAA8B;AACnCD,IAAAA,OAAO,gBAAGE,eAAMC,aAAN,CAAoBE,iCAApB,EAA8CN,KAA9C,CAAV;AACD,GAFM,MAEA;AACLC,IAAAA,OAAO,gBAAGE,eAAMC,aAAN,CAAoBG,+BAApB,EAA4CP,KAA5C,CAAV;AACD;;AACD,sBACE,6BAAC,4BAAD;AACE,IAAA,MAAM,EAAED,KAAK,CAACS;AADhB,KAEMT,KAAK,CAACU,cAFZ;AAGE,IAAA,OAAO,EAAEV,KAAK,CAACW,OAHjB;AAIE,IAAA,KAAK,EAAEV,KAAK,CAACW;AAJf,MAKGV,OALH,CADF;AASD;;eAEcH,U","sourcesContent":["import React, { ReactElement, ReactNode } from 'react';\nimport useIMGElementState from './useIMGElementState';\nimport IMGElementContentSuccess from './IMGElementContentSuccess';\nimport IMGElementContainer from './IMGElementContainer';\nimport IMGElementContentLoading from './IMGElementContentLoading';\nimport IMGElementContentError from './IMGElementContentError';\nimport type { IMGElementProps } from './img-types';\n\nexport type { IMGElementProps } from './img-types';\n\n/**\n * A component to render images based on an internal loading state.\n *\n * @remarks This component will attempt to draw a box of paint dimensions\n * before retrieving the physical dimensions of the image to avoid layout\n * shifts. See also {@link useIMGElementState}, {@link IMGElementContainer},\n * {@link IMGElementContentSuccess}, {@link IMGElementContentLoading}\n * and {@link IMGElementContentError} for customization.\n */\nfunction IMGElement(props: IMGElementProps): ReactElement {\n const state = useIMGElementState(props);\n let content: ReactNode;\n if (state.type === 'success') {\n content = React.createElement(IMGElementContentSuccess, state);\n } else if (state.type === 'loading') {\n content = React.createElement(IMGElementContentLoading, state);\n } else {\n content = React.createElement(IMGElementContentError, state);\n }\n return (\n \n {content}\n \n );\n}\n\nexport default IMGElement;\n"]} +\ No newline at end of file diff --git a/node_modules/react-native-render-html/react-native-render-html.podspec b/node_modules/react-native-render-html/react-native-render-html.podspec deleted file mode 100644 index 6ac80d8..0000000 @@ -23,3 +359,407 @@ index 6ac80d8..0000000 - - s.dependency 'React-Core' -end +diff --git a/node_modules/react-native-render-html/src/RenderHTMLConfigProvider.tsx b/node_modules/react-native-render-html/src/RenderHTMLConfigProvider.tsx +index 0df5375..925062a 100644 +--- a/node_modules/react-native-render-html/src/RenderHTMLConfigProvider.tsx ++++ b/node_modules/react-native-render-html/src/RenderHTMLConfigProvider.tsx +@@ -1,5 +1,4 @@ + import React, { PropsWithChildren, ReactElement, useMemo } from 'react'; +-import PropTypes from 'prop-types'; + import RenderersPropsProvider from './context/RenderersPropsProvider'; + import SharedPropsProvider from './context/SharedPropsProvider'; + import TChildrenRenderersContext from './context/TChildrenRendererContext'; +@@ -20,29 +19,6 @@ const childrenRendererContext = { + TNodeChildrenRenderer + }; + +-export type RenderHTMLConfigPropTypes = Record; +- +-export const renderHTMLConfigPropTypes: RenderHTMLConfigPropTypes = { +- bypassAnonymousTPhrasingNodes: PropTypes.bool, +- defaultTextProps: PropTypes.object, +- defaultViewProps: PropTypes.object, +- enableExperimentalBRCollapsing: PropTypes.bool, +- enableExperimentalGhostLinesPrevention: PropTypes.bool, +- enableExperimentalMarginCollapsing: PropTypes.bool, +- remoteErrorView: PropTypes.func, +- remoteLoadingView: PropTypes.func, +- debug: PropTypes.bool, +- computeEmbeddedMaxWidth: PropTypes.func, +- renderersProps: PropTypes.object, +- WebView: PropTypes.any, +- GenericPressable: PropTypes.any, +- defaultWebViewProps: PropTypes.object, +- pressableHightlightColor: PropTypes.string, +- customListStyleSpecs: PropTypes.object, +- renderers: PropTypes.object, +- provideEmbeddedHeaders: PropTypes.func +-}; +- + /** + * A component to provide configuration for {@link RenderHTMLSource} + * descendants, to be used in conjunction with {@link TRenderEngineProvider}. +@@ -85,8 +61,3 @@ export default function RenderHTMLConfigProvider( + + ); + } +- +-/** +- * @ignore +- */ +-RenderHTMLConfigProvider.propTypes = renderHTMLConfigPropTypes; +diff --git a/node_modules/react-native-render-html/src/RenderHTMLSource.tsx b/node_modules/react-native-render-html/src/RenderHTMLSource.tsx +index c91da52..fd0e052 100644 +--- a/node_modules/react-native-render-html/src/RenderHTMLSource.tsx ++++ b/node_modules/react-native-render-html/src/RenderHTMLSource.tsx +@@ -1,7 +1,6 @@ + import equals from 'ramda/src/equals'; + import React, { memo, ReactElement, useMemo } from 'react'; + import { Dimensions } from 'react-native'; +-import PropTypes from 'prop-types'; + import ttreeEventsContext from './context/ttreeEventsContext'; + import isUriSource from './helpers/isUriSource'; + import { SourceLoaderProps, TTreeEvents } from './internal-types'; +@@ -25,29 +24,6 @@ export type RenderHTMLSourcePropTypes = Record< + any + >; + +-export const renderSourcePropTypes: RenderHTMLSourcePropTypes = { +- source: PropTypes.oneOfType([ +- PropTypes.shape({ +- html: PropTypes.string.isRequired, +- baseUrl: PropTypes.string +- }), +- PropTypes.shape({ +- dom: PropTypes.object.isRequired, +- baseUrl: PropTypes.string +- }), +- PropTypes.shape({ +- uri: PropTypes.string.isRequired, +- method: PropTypes.string, +- body: PropTypes.any, +- headers: PropTypes.object +- }) +- ]), +- onTTreeChange: PropTypes.func, +- onHTMLLoaded: PropTypes.func, +- onDocumentMetadataLoaded: PropTypes.func, +- contentWidth: PropTypes.number +-}; +- + function isEmptySource(source: undefined | HTMLSource) { + return ( + !source || +@@ -136,9 +112,4 @@ const RenderHTMLSource = memo( + } + ); + +-/** +- * @ignore +- */ +-(RenderHTMLSource as any).propTypes = renderSourcePropTypes; +- + export default RenderHTMLSource; +diff --git a/node_modules/react-native-render-html/src/TChildrenRenderer.tsx b/node_modules/react-native-render-html/src/TChildrenRenderer.tsx +index 618a592..e12888e 100644 +--- a/node_modules/react-native-render-html/src/TChildrenRenderer.tsx ++++ b/node_modules/react-native-render-html/src/TChildrenRenderer.tsx +@@ -9,16 +9,4 @@ import renderChildren from './renderChildren'; + const TChildrenRenderer: FunctionComponent = + renderChildren.bind(null); + +-export const tchildrenRendererDefaultProps: Pick< +- TChildrenRendererProps, +- 'propsForChildren' +-> = { +- propsForChildren: {} +-}; +- +-/** +- * @ignore +- */ +-TChildrenRenderer.defaultProps = tchildrenRendererDefaultProps; +- + export default TChildrenRenderer; +diff --git a/node_modules/react-native-render-html/src/TNodeChildrenRenderer.tsx b/node_modules/react-native-render-html/src/TNodeChildrenRenderer.tsx +index bf5aef6..b820de0 100644 +--- a/node_modules/react-native-render-html/src/TNodeChildrenRenderer.tsx ++++ b/node_modules/react-native-render-html/src/TNodeChildrenRenderer.tsx +@@ -1,7 +1,6 @@ + import { ReactElement } from 'react'; + import { TNode } from '@native-html/transient-render-engine'; + import { useSharedProps } from './context/SharedPropsProvider'; +-import { tchildrenRendererDefaultProps } from './TChildrenRenderer'; + import { + TChildrenRendererProps, + TNodeChildrenRendererProps +@@ -73,9 +72,4 @@ function TNodeChildrenRenderer( + return renderChildren(useTNodeChildrenProps(props)); + } + +-/** +- * @ignore +- */ +-TNodeChildrenRenderer.defaultProps = tchildrenRendererDefaultProps; +- + export default TNodeChildrenRenderer; +diff --git a/node_modules/react-native-render-html/src/TNodeRenderer.tsx b/node_modules/react-native-render-html/src/TNodeRenderer.tsx +index d32140f..0804ba7 100644 +--- a/node_modules/react-native-render-html/src/TNodeRenderer.tsx ++++ b/node_modules/react-native-render-html/src/TNodeRenderer.tsx +@@ -49,6 +49,7 @@ const TNodeRenderer = memo(function MemoizedTNodeRenderer( + const renderRegistry = useRendererRegistry(); + const TNodeChildrenRenderer = useTNodeChildrenRenderer(); + const tnodeProps = { ++ propsFromParent: { collapsedMarginTop: null }, + ...props, + TNodeChildrenRenderer, + sharedProps +@@ -120,16 +121,6 @@ const TNodeRenderer = memo(function MemoizedTNodeRenderer( + : React.createElement(Renderer as any, assembledProps); + }); + +-const defaultProps: Required, 'propsFromParent'>> = +- { +- propsFromParent: { +- collapsedMarginTop: null +- } +- }; +- +-// @ts-expect-error default props must be defined +-TNodeRenderer.defaultProps = defaultProps; +- + export { + TDefaultBlockRenderer, + TDefaultPhrasingRenderer, +diff --git a/node_modules/react-native-render-html/src/TRenderEngineProvider.tsx b/node_modules/react-native-render-html/src/TRenderEngineProvider.tsx +index 95b60df..96604c8 100644 +--- a/node_modules/react-native-render-html/src/TRenderEngineProvider.tsx ++++ b/node_modules/react-native-render-html/src/TRenderEngineProvider.tsx +@@ -1,73 +1,13 @@ + import TRenderEngine from '@native-html/transient-render-engine'; + import React, { PropsWithChildren, ReactElement } from 'react'; +-import { Platform } from 'react-native'; +-import PropTypes from 'prop-types'; + import useTRenderEngine from './hooks/useTRenderEngine'; + import { TRenderEngineConfig } from './shared-types'; +-import defaultSystemFonts from './defaultSystemFonts'; + + const defaultTRenderEngine = {} as any; + + const TRenderEngineContext = + React.createContext(defaultTRenderEngine); + +-export const tRenderEngineProviderPropTypes: Record< +- keyof TRenderEngineConfig, +- any +-> = { +- customHTMLElementModels: PropTypes.object.isRequired, +- enableCSSInlineProcessing: PropTypes.bool, +- enableUserAgentStyles: PropTypes.bool, +- idsStyles: PropTypes.object, +- ignoredDomTags: PropTypes.array, +- ignoreDomNode: PropTypes.func, +- domVisitors: PropTypes.object, +- ignoredStyles: PropTypes.array.isRequired, +- allowedStyles: PropTypes.array, +- htmlParserOptions: PropTypes.object, +- tagsStyles: PropTypes.object, +- classesStyles: PropTypes.object, +- emSize: PropTypes.number.isRequired, +- baseStyle: PropTypes.object, +- systemFonts: PropTypes.arrayOf(PropTypes.string), +- fallbackFonts: PropTypes.shape({ +- serif: PropTypes.string, +- 'sans-serif': PropTypes.string, +- monospace: PropTypes.string +- }), +- setMarkersForTNode: PropTypes.func, +- dangerouslyDisableHoisting: PropTypes.bool, +- dangerouslyDisableWhitespaceCollapsing: PropTypes.bool, +- selectDomRoot: PropTypes.func +-}; +- +-/** +- * Default fallback font for special keys such as 'sans-serif', 'monospace', +- * 'serif', based on current platform. +- */ +-export const defaultFallbackFonts = { +- 'sans-serif': Platform.select({ ios: 'system', default: 'sans-serif' }), +- monospace: Platform.select({ ios: 'Menlo', default: 'monospace' }), +- serif: Platform.select({ ios: 'Times New Roman', default: 'serif' }) +-}; +- +-export const defaultTRenderEngineProviderProps: TRenderEngineConfig = { +- htmlParserOptions: { +- decodeEntities: true +- }, +- emSize: 14, +- ignoredDomTags: [], +- ignoredStyles: [], +- baseStyle: { fontSize: 14 }, +- tagsStyles: {}, +- classesStyles: {}, +- enableUserAgentStyles: true, +- enableCSSInlineProcessing: true, +- customHTMLElementModels: {}, +- fallbackFonts: defaultFallbackFonts, +- systemFonts: defaultSystemFonts +-}; +- + /** + * Use the ambient transient render engine. + * +@@ -106,13 +46,3 @@ export default function TRenderEngineProvider({ + + ); + } +- +-/** +- * @ignore +- */ +-TRenderEngineProvider.defaultProps = defaultTRenderEngineProviderProps; +- +-/** +- * @ignore +- */ +-TRenderEngineProvider.propTypes = tRenderEngineProviderPropTypes; +diff --git a/node_modules/react-native-render-html/src/elements/IMGElement.tsx b/node_modules/react-native-render-html/src/elements/IMGElement.tsx +index 573e7c1..a6fc90b 100644 +--- a/node_modules/react-native-render-html/src/elements/IMGElement.tsx ++++ b/node_modules/react-native-render-html/src/elements/IMGElement.tsx +@@ -1,19 +1,13 @@ + import React, { ReactElement, ReactNode } from 'react'; +-import PropTypes from 'prop-types'; + import useIMGElementState from './useIMGElementState'; + import IMGElementContentSuccess from './IMGElementContentSuccess'; + import IMGElementContainer from './IMGElementContainer'; + import IMGElementContentLoading from './IMGElementContentLoading'; + import IMGElementContentError from './IMGElementContentError'; + import type { IMGElementProps } from './img-types'; +-import defaultImageInitialDimensions from './defaultInitialImageDimensions'; + + export type { IMGElementProps } from './img-types'; + +-function identity(arg: any) { +- return arg; +-} +- + /** + * A component to render images based on an internal loading state. + * +@@ -44,42 +38,4 @@ function IMGElement(props: IMGElementProps): ReactElement { + ); + } + +-const imgDimensionsType = PropTypes.shape({ +- width: PropTypes.number, +- height: PropTypes.number +-}); +- +-const propTypes: Record = { +- source: PropTypes.object.isRequired, +- alt: PropTypes.string, +- altColor: PropTypes.string, +- height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), +- width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), +- style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]), +- computeMaxWidth: PropTypes.func.isRequired, +- contentWidth: PropTypes.number, +- enableExperimentalPercentWidth: PropTypes.bool, +- initialDimensions: imgDimensionsType, +- onPress: PropTypes.func, +- testID: PropTypes.string, +- objectFit: PropTypes.string, +- cachedNaturalDimensions: imgDimensionsType, +- containerProps: PropTypes.object +-}; +- +-/** +- * @ignore +- */ +-IMGElement.propTypes = propTypes; +- +-/** +- * @ignore +- */ +-IMGElement.defaultProps = { +- enableExperimentalPercentWidth: false, +- computeMaxWidth: identity, +- imagesInitialDimensions: defaultImageInitialDimensions, +- style: {} +-}; +- + export default IMGElement; +diff --git a/node_modules/react-native-render-html/src/elements/useIMGElementState.ts b/node_modules/react-native-render-html/src/elements/useIMGElementState.ts +index 6590d21..b603f26 100644 +--- a/node_modules/react-native-render-html/src/elements/useIMGElementState.ts ++++ b/node_modules/react-native-render-html/src/elements/useIMGElementState.ts +@@ -63,6 +63,10 @@ function useImageNaturalDimensions

(props: { + }; + } + ++function identity(arg: any) { ++ return arg; ++} ++ + function useFetchedNaturalDimensions(props: { + cachedNaturalDimensions?: ImageDimensions; + source: ImageURISource; +@@ -116,7 +120,7 @@ export default function useIMGElementState( + altColor, + source, + contentWidth, +- computeMaxWidth, ++ computeMaxWidth = identity, + objectFit, + initialDimensions = defaultImageInitialDimensions, + cachedNaturalDimensions +diff --git a/node_modules/react-native-render-html/src/elements/useImageSpecifiedDimensions.ts b/node_modules/react-native-render-html/src/elements/useImageSpecifiedDimensions.ts +index 5d6271b..710c73f 100644 +--- a/node_modules/react-native-render-html/src/elements/useImageSpecifiedDimensions.ts ++++ b/node_modules/react-native-render-html/src/elements/useImageSpecifiedDimensions.ts +@@ -71,8 +71,7 @@ function deriveSpecifiedDimensionsFromProps({ + export default function useImageSpecifiedDimensions( + props: UseIMGElementStateProps + ) { +- const { contentWidth, enableExperimentalPercentWidth, style, width, height } = +- props; ++ const { contentWidth, enableExperimentalPercentWidth = false, style = {}, width, height } = props + const flatStyle = useMemo(() => StyleSheet.flatten(style) || {}, [style]); + const specifiedDimensions = useMemo( + () => +diff --git a/node_modules/react-native-render-html/src/index.ts b/node_modules/react-native-render-html/src/index.ts +index 8569583..b59ec49 100644 +--- a/node_modules/react-native-render-html/src/index.ts ++++ b/node_modules/react-native-render-html/src/index.ts +@@ -128,7 +128,6 @@ export { + export { default as TNodeRenderer } from './TNodeRenderer'; + export { + default as TRenderEngineProvider, +- defaultFallbackFonts, + useAmbientTRenderEngine + } from './TRenderEngineProvider'; + export { default as RenderHTMLConfigProvider } from './RenderHTMLConfigProvider'; +diff --git a/node_modules/react-native-render-html/src/renderChildren.tsx b/node_modules/react-native-render-html/src/renderChildren.tsx +index a669402..be9ffd6 100644 +--- a/node_modules/react-native-render-html/src/renderChildren.tsx ++++ b/node_modules/react-native-render-html/src/renderChildren.tsx +@@ -4,8 +4,6 @@ import TNodeRenderer from './TNodeRenderer'; + import { TChildrenRendererProps } from './shared-types'; + import collapseTopMarginForChild from './helpers/collapseTopMarginForChild'; + +-const empty = {}; +- + const mapCollapsibleChildren = ( + propsForChildren: TChildrenRendererProps['propsForChildren'], + renderChild: TChildrenRendererProps['renderChild'], +@@ -39,7 +37,7 @@ const mapCollapsibleChildren = ( + + export default function renderChildren({ + tchildren, +- propsForChildren = empty, ++ propsForChildren = {}, + disableMarginCollapsing, + renderChild + }: TChildrenRendererProps): ReactElement { From ee7c1f5a65fae14e2cee8cda31ff631b446b42f7 Mon Sep 17 00:00:00 2001 From: Tomasz Lesniakiewicz Date: Thu, 21 Nov 2024 19:17:07 +0100 Subject: [PATCH 011/240] fix lint --- src/components/ParentNavigationSubtitle.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ParentNavigationSubtitle.tsx b/src/components/ParentNavigationSubtitle.tsx index 9edf73e5f36f..c80e8a8caf1a 100644 --- a/src/components/ParentNavigationSubtitle.tsx +++ b/src/components/ParentNavigationSubtitle.tsx @@ -61,7 +61,7 @@ function ParentNavigationSubtitle({parentNavigationSubtitleData, parentReportAct {reportName} )} - {workspaceName && workspaceName !== reportName && ( + {!!workspaceName && workspaceName !== reportName && ( {` ${translate('threads.in')} ${workspaceName}`} )} From 8758645b14ce54171a6da2d5f946662ffa155866 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Fri, 22 Nov 2024 11:58:48 +0100 Subject: [PATCH 012/240] split the patch into two files --- ...native-render-html+6.3.1+001+initial.patch | 25 +++++++++++++++++++ ...-html+6.3.1+002+fix-console-warning.patch} | 25 ------------------- 2 files changed, 25 insertions(+), 25 deletions(-) create mode 100644 patches/react-native-render-html+6.3.1+001+initial.patch rename patches/{react-native-render-html+6.3.1.patch => react-native-render-html+6.3.1+002+fix-console-warning.patch} (98%) diff --git a/patches/react-native-render-html+6.3.1+001+initial.patch b/patches/react-native-render-html+6.3.1+001+initial.patch new file mode 100644 index 000000000000..a14965af4caf --- /dev/null +++ b/patches/react-native-render-html+6.3.1+001+initial.patch @@ -0,0 +1,25 @@ +diff --git a/node_modules/react-native-render-html/react-native-render-html.podspec b/node_modules/react-native-render-html/react-native-render-html.podspec +deleted file mode 100644 +index 6ac80d8..0000000 +--- a/node_modules/react-native-render-html/react-native-render-html.podspec ++++ /dev/null +@@ -1,19 +0,0 @@ +-require 'json' +- +-package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) +- +-Pod::Spec.new do |s| +- s.name = package['name'] +- s.version = package['version'] +- s.summary = package['description'] +- s.license = package['license'] +- +- s.authors = package['author'] +- s.homepage = package['homepage'] +- s.platform = :ios, "9.0" +- +- s.source = { :git => "https://github.com/meliorence/react-native-render-html.git", :tag => "v#{s.version}" } +- s.source_files = "ios/**/*.{h,m}" +- +- s.dependency 'React-Core' +-end \ No newline at end of file diff --git a/patches/react-native-render-html+6.3.1.patch b/patches/react-native-render-html+6.3.1+002+fix-console-warning.patch similarity index 98% rename from patches/react-native-render-html+6.3.1.patch rename to patches/react-native-render-html+6.3.1+002+fix-console-warning.patch index 0d2c2496f568..e7a5cd3b230a 100644 --- a/patches/react-native-render-html+6.3.1.patch +++ b/patches/react-native-render-html+6.3.1+002+fix-console-warning.patch @@ -334,31 +334,6 @@ index 1e265b3..24ec0d0 100644 \ No newline at end of file +{"version":3,"sources":["IMGElement.tsx"],"names":["IMGElement","props","state","content","type","React","createElement","IMGElementContentSuccess","IMGElementContentLoading","IMGElementContentError","testID","containerProps","onPress","containerStyle"],"mappings":";;;;;;;AAAA;;AACA;;AACA;;AACA;;AACA;;AACA;;;;;;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASA,UAAT,CAAoBC,KAApB,EAA0D;AACxD,QAAMC,KAAK,GAAG,iCAAmBD,KAAnB,CAAd;AACA,MAAIE,OAAJ;;AACA,MAAID,KAAK,CAACE,IAAN,KAAe,SAAnB,EAA8B;AAC5BD,IAAAA,OAAO,gBAAGE,eAAMC,aAAN,CAAoBC,iCAApB,EAA8CL,KAA9C,CAAV;AACD,GAFD,MAEO,IAAIA,KAAK,CAACE,IAAN,KAAe,SAAnB,EAA8B;AACnCD,IAAAA,OAAO,gBAAGE,eAAMC,aAAN,CAAoBE,iCAApB,EAA8CN,KAA9C,CAAV;AACD,GAFM,MAEA;AACLC,IAAAA,OAAO,gBAAGE,eAAMC,aAAN,CAAoBG,+BAApB,EAA4CP,KAA5C,CAAV;AACD;;AACD,sBACE,6BAAC,4BAAD;AACE,IAAA,MAAM,EAAED,KAAK,CAACS;AADhB,KAEMT,KAAK,CAACU,cAFZ;AAGE,IAAA,OAAO,EAAEV,KAAK,CAACW,OAHjB;AAIE,IAAA,KAAK,EAAEV,KAAK,CAACW;AAJf,MAKGV,OALH,CADF;AASD;;eAEcH,U","sourcesContent":["import React, { ReactElement, ReactNode } from 'react';\nimport useIMGElementState from './useIMGElementState';\nimport IMGElementContentSuccess from './IMGElementContentSuccess';\nimport IMGElementContainer from './IMGElementContainer';\nimport IMGElementContentLoading from './IMGElementContentLoading';\nimport IMGElementContentError from './IMGElementContentError';\nimport type { IMGElementProps } from './img-types';\n\nexport type { IMGElementProps } from './img-types';\n\n/**\n * A component to render images based on an internal loading state.\n *\n * @remarks This component will attempt to draw a box of paint dimensions\n * before retrieving the physical dimensions of the image to avoid layout\n * shifts. See also {@link useIMGElementState}, {@link IMGElementContainer},\n * {@link IMGElementContentSuccess}, {@link IMGElementContentLoading}\n * and {@link IMGElementContentError} for customization.\n */\nfunction IMGElement(props: IMGElementProps): ReactElement {\n const state = useIMGElementState(props);\n let content: ReactNode;\n if (state.type === 'success') {\n content = React.createElement(IMGElementContentSuccess, state);\n } else if (state.type === 'loading') {\n content = React.createElement(IMGElementContentLoading, state);\n } else {\n content = React.createElement(IMGElementContentError, state);\n }\n return (\n \n {content}\n \n );\n}\n\nexport default IMGElement;\n"]} \ No newline at end of file -diff --git a/node_modules/react-native-render-html/react-native-render-html.podspec b/node_modules/react-native-render-html/react-native-render-html.podspec -deleted file mode 100644 -index 6ac80d8..0000000 ---- a/node_modules/react-native-render-html/react-native-render-html.podspec -+++ /dev/null -@@ -1,19 +0,0 @@ --require 'json' -- --package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) -- --Pod::Spec.new do |s| -- s.name = package['name'] -- s.version = package['version'] -- s.summary = package['description'] -- s.license = package['license'] -- -- s.authors = package['author'] -- s.homepage = package['homepage'] -- s.platform = :ios, "9.0" -- -- s.source = { :git => "https://github.com/meliorence/react-native-render-html.git", :tag => "v#{s.version}" } -- s.source_files = "ios/**/*.{h,m}" -- -- s.dependency 'React-Core' --end diff --git a/node_modules/react-native-render-html/src/RenderHTMLConfigProvider.tsx b/node_modules/react-native-render-html/src/RenderHTMLConfigProvider.tsx index 0df5375..925062a 100644 --- a/node_modules/react-native-render-html/src/RenderHTMLConfigProvider.tsx From 4628a5b82f8f1056844587536e2dd70c466f073c Mon Sep 17 00:00:00 2001 From: Anas Up Date: Fri, 22 Nov 2024 06:12:48 -0500 Subject: [PATCH 013/240] wrap scrollToBottomForCurrentUserAction with InteractionManager.runAfterInteractions --- src/pages/home/report/ReportActionsList.tsx | 24 +++++++++++---------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index 57d3c48e3b61..cf0536ca07b5 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -421,19 +421,21 @@ function ReportActionsList({ const scrollToBottomForCurrentUserAction = useCallback( (isFromCurrentUser: boolean) => { - // If a new comment is added and it's from the current user scroll to the bottom otherwise leave the user positioned where - // they are now in the list. - if (!isFromCurrentUser) { - return; - } - if (!hasNewestReportActionRef.current) { - if (isInNarrowPaneModal) { + InteractionManager.runAfterInteractions(() => { + // If a new comment is added and it's from the current user scroll to the bottom otherwise leave the user positioned where + // they are now in the list. + if (!isFromCurrentUser) { return; } - Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(report.reportID)); - return; - } - InteractionManager.runAfterInteractions(() => reportScrollManager.scrollToBottom()); + if (!hasNewestReportActionRef.current) { + if (isInNarrowPaneModal) { + return; + } + Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(report.reportID)); + return; + } + reportScrollManager.scrollToBottom(); + }); }, [isInNarrowPaneModal, reportScrollManager, report.reportID], ); From a33274b0f14aca64e8b9d5d337efac86670249f2 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 20 Nov 2024 10:11:36 +0100 Subject: [PATCH 014/240] Create trip details screen --- src/ROUTES.ts | 4 +++ src/SCREENS.ts | 1 + .../ModalStackNavigators/index.tsx | 1 + src/libs/Navigation/linkingConfig/config.ts | 1 + src/pages/Travel/TripDetails.tsx | 36 +++++++++++++++++++ 5 files changed, 43 insertions(+) create mode 100644 src/pages/Travel/TripDetails.tsx diff --git a/src/ROUTES.ts b/src/ROUTES.ts index d8f8b0f91105..f324d4af6d2e 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -1343,6 +1343,10 @@ const ROUTES = { TRAVEL_MY_TRIPS: 'travel', TRAVEL_TCS: 'travel/terms', TRACK_TRAINING_MODAL: 'track-training', + TRAVEL_TRIP_DETAILS: { + route: 'r/:reportID/trip/:transactionID', + getRoute: (reportID: string, transactionID: string) => `r/${reportID}/trip/${transactionID}` as const, + }, ONBOARDING_ROOT: { route: 'onboarding', getRoute: (backTo?: string) => getUrlWithBackToParam(`onboarding`, backTo), diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 5fd64b0fc0d0..f6a53da3f199 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -27,6 +27,7 @@ const SCREENS = { TRAVEL: { MY_TRIPS: 'Travel_MyTrips', TCS: 'Travel_TCS', + TRIP_DETAILS: 'Trip_Details', }, SEARCH: { CENTRAL_PANE: 'Search_Central_Pane', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 9295281755e5..05bec49e9bd9 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -105,6 +105,7 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator({ [SCREENS.TRAVEL.MY_TRIPS]: () => require('../../../../pages/Travel/MyTripsPage').default, [SCREENS.TRAVEL.TCS]: () => require('../../../../pages/Travel/TravelTerms').default, + [SCREENS.TRAVEL.TRIP_DETAILS]: () => require('../../../pages/TripDetails').default, }); const SplitDetailsModalStackNavigator = createModalStackNavigator({ diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 476711c7c116..2d1511789c24 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -1320,6 +1320,7 @@ const config: LinkingOptions['config'] = { screens: { [SCREENS.TRAVEL.MY_TRIPS]: ROUTES.TRAVEL_MY_TRIPS, [SCREENS.TRAVEL.TCS]: ROUTES.TRAVEL_TCS, + [SCREENS.TRAVEL.TRIP_DETAILS]: ROUTES.TRAVEL_TRIP_DETAILS, }, }, [SCREENS.RIGHT_MODAL.SEARCH_REPORT]: { diff --git a/src/pages/Travel/TripDetails.tsx b/src/pages/Travel/TripDetails.tsx new file mode 100644 index 000000000000..8c3d1bbadfc1 --- /dev/null +++ b/src/pages/Travel/TripDetails.tsx @@ -0,0 +1,36 @@ +import React from 'react'; +import {NativeModules} from 'react-native'; +import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import ScreenWrapper from '@components/ScreenWrapper'; +import useLocalize from '@hooks/useLocalize'; +import usePermissions from '@hooks/usePermissions'; + +function TripDetails() { + const {translate} = useLocalize(); + const {canUseSpotnanaTravel} = usePermissions(); + + return ( + + + + + + ); +} + +TripDetails.displayName = 'TripDetails'; + +export default TripDetails; From 15c9ae36fc845f298078a1f28939064158088b7b Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 20 Nov 2024 10:11:49 +0100 Subject: [PATCH 015/240] Add translation for trip support --- src/languages/en.ts | 1 + src/languages/es.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/languages/en.ts b/src/languages/en.ts index c1067e195985..ec0e49353bbb 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2424,6 +2424,7 @@ const translations = { hotel: 'Hotel', car: 'Car', viewTrip: 'View trip', + tripSupport: 'Trip support', viewTripDetails: 'View trip details', trip: 'Trip', trips: 'Trips', diff --git a/src/languages/es.ts b/src/languages/es.ts index f7af1be45139..623afb1ff16a 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2448,6 +2448,7 @@ const translations = { hotel: 'Hotel', car: 'Auto', viewTrip: 'Ver viaje', + tripSupport: 'Soporte de Viaje', viewTripDetails: 'Ver detalles del viaje', trip: 'Viaje', trips: 'Viajes', From 3ae7f986f7fee31a3f065e2b2be31aece0c3ed49 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 20 Nov 2024 10:16:31 +0100 Subject: [PATCH 016/240] Add undefined type for trip details --- src/libs/Navigation/types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 5877c9c10218..caa5ff5d1a06 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1380,6 +1380,7 @@ type RightModalNavigatorParamList = { type TravelNavigatorParamList = { [SCREENS.TRAVEL.MY_TRIPS]: undefined; + [SCREENS.TRAVEL.TRIP_DETAILS]: undefined; }; type FullScreenNavigatorParamList = { From d2e1df5cf70c7bf112174a58369352fff890b69e Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 20 Nov 2024 12:39:33 +0100 Subject: [PATCH 017/240] Fix navigation config --- src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx | 2 +- src/libs/Navigation/linkingConfig/config.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 05bec49e9bd9..beb030ccb09d 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -105,7 +105,7 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator({ [SCREENS.TRAVEL.MY_TRIPS]: () => require('../../../../pages/Travel/MyTripsPage').default, [SCREENS.TRAVEL.TCS]: () => require('../../../../pages/Travel/TravelTerms').default, - [SCREENS.TRAVEL.TRIP_DETAILS]: () => require('../../../pages/TripDetails').default, + [SCREENS.TRAVEL.TRIP_DETAILS]: () => require('../../../../pages/Travel/TripDetails').default, }); const SplitDetailsModalStackNavigator = createModalStackNavigator({ diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 2d1511789c24..317c1e15e04f 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -1320,7 +1320,7 @@ const config: LinkingOptions['config'] = { screens: { [SCREENS.TRAVEL.MY_TRIPS]: ROUTES.TRAVEL_MY_TRIPS, [SCREENS.TRAVEL.TCS]: ROUTES.TRAVEL_TCS, - [SCREENS.TRAVEL.TRIP_DETAILS]: ROUTES.TRAVEL_TRIP_DETAILS, + [SCREENS.TRAVEL.TRIP_DETAILS]: ROUTES.TRAVEL_TRIP_DETAILS.route, }, }, [SCREENS.RIGHT_MODAL.SEARCH_REPORT]: { From 7394a09389a39b499723e05548285e930ea34b9f Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 22 Nov 2024 14:19:35 +0100 Subject: [PATCH 018/240] Link the correct backTo on trip details screen --- src/ROUTES.ts | 2 +- src/components/ReportActionItem/MoneyRequestView.tsx | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index f324d4af6d2e..920e328ebfff 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -1345,7 +1345,7 @@ const ROUTES = { TRACK_TRAINING_MODAL: 'track-training', TRAVEL_TRIP_DETAILS: { route: 'r/:reportID/trip/:transactionID', - getRoute: (reportID: string, transactionID: string) => `r/${reportID}/trip/${transactionID}` as const, + getRoute: (reportID: string, transactionID: string, backTo?: string) => getUrlWithBackToParam(`r/${reportID}/trip/${transactionID}`, backTo), }, ONBOARDING_ROOT: { route: 'onboarding', diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 381f01aadd89..1533a7515a98 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -34,7 +34,6 @@ import ViolationsUtils from '@libs/Violations/ViolationsUtils'; import Navigation from '@navigation/Navigation'; import AnimatedEmptyStateBackground from '@pages/home/report/AnimatedEmptyStateBackground'; import * as IOU from '@userActions/IOU'; -import * as Link from '@userActions/Link'; import * as Transaction from '@userActions/Transaction'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; @@ -84,7 +83,6 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals const session = useSession(); const {isOffline} = useNetwork(); const {translate, toLocaleDigit} = useLocalize(); - const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID); const parentReportID = report?.parentReportID ?? '-1'; const policyID = report?.policyID ?? '-1'; const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${parentReportID}`); @@ -696,11 +694,9 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals { - Link.openTravelDotLink(activePolicyID, CONST.TRIP_ID_PATH(tripID)); - }} + onPress={() => + Navigation.navigate(ROUTES.TRAVEL_TRIP_DETAILS.getRoute(report?.reportID ?? '-1', transaction?.transactionID ?? '-1', Navigation.getReportRHPActiveRoute())) + } /> )} {shouldShowAttendees && ( From 2c9d2a8c72caa65b5b28a7f5a2294ae00ea975d5 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 22 Nov 2024 16:21:06 +0100 Subject: [PATCH 019/240] Access data and fix getTripIDFromTransactionParentReportID --- src/CONST.ts | 2 +- .../ReportActionItem/MoneyRequestView.tsx | 2 +- src/libs/Navigation/types.ts | 6 ++- src/libs/ReportUtils.ts | 6 +-- src/pages/Travel/TripDetails.tsx | 50 ++++++++++++++++++- 5 files changed, 58 insertions(+), 8 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index ee70e3b29668..80b52c65bfcf 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -4427,7 +4427,7 @@ const CONST = { BOOK_TRAVEL_DEMO_URL: 'https://calendly.com/d/ck2z-xsh-q97/expensify-travel-demo-travel-page', TRAVEL_DOT_URL: 'https://travel.expensify.com', STAGING_TRAVEL_DOT_URL: 'https://staging.travel.expensify.com', - TRIP_ID_PATH: (tripID: string) => `trips/${tripID}`, + TRIP_ID_PATH: (tripID?: string) => (tripID ? `trips/${tripID}` : undefined), SPOTNANA_TMC_ID: '8e8e7258-1cf3-48c0-9cd1-fe78a6e31eed', STAGING_SPOTNANA_TMC_ID: '7a290c6e-5328-4107-aff6-e48765845b81', SCREEN_READER_STATES: { diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 1533a7515a98..6bc356dd6f15 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -186,7 +186,7 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals const shouldShowAttendees = useMemo(() => TransactionUtils.shouldShowAttendees(iouType, policy), [iouType, policy]); const shouldShowTax = isTaxTrackingEnabled(isPolicyExpenseChat, policy, isDistanceRequest); - const tripID = ReportUtils.getTripIDFromTransactionParentReport(parentReport); + const tripID = ReportUtils.getTripIDFromTransactionParentReportID(parentReport?.parentReportID); const shouldShowViewTripDetails = TransactionUtils.hasReservationList(transaction) && !!tripID; const {getViolationsForField} = useViolations(transactionViolations ?? [], isReceiptBeingScanned || !ReportUtils.isPaidGroupPolicy(report)); diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index caa5ff5d1a06..469d20d15bbc 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1380,7 +1380,11 @@ type RightModalNavigatorParamList = { type TravelNavigatorParamList = { [SCREENS.TRAVEL.MY_TRIPS]: undefined; - [SCREENS.TRAVEL.TRIP_DETAILS]: undefined; + [SCREENS.TRAVEL.TRIP_DETAILS]: { + reportID: string; + transactionID: string; + backTo?: string; + }; }; type FullScreenNavigatorParamList = { diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 36095b7d88ec..72f11f4a3150 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -8030,8 +8030,8 @@ function getTripTransactions(tripRoomReportID: string | undefined, reportFieldTo return tripTransactionReportIDs.flatMap((reportID) => reportsTransactions[reportID ?? ''] ?? []); } -function getTripIDFromTransactionParentReport(transactionParentReport: OnyxEntry | undefined | null): string | undefined { - return getReportOrDraftReport(transactionParentReport?.parentReportID)?.tripData?.tripID; +function getTripIDFromTransactionParentReportID(transactionParentReportID: string | undefined): string | undefined { + return getReportOrDraftReport(transactionParentReportID)?.tripData?.tripID; } /** @@ -8700,7 +8700,7 @@ export { updateReportPreview, temporary_getMoneyRequestOptions, getTripTransactions, - getTripIDFromTransactionParentReport, + getTripIDFromTransactionParentReportID, buildOptimisticInvoiceReport, getInvoiceChatByParticipants, shouldShowMerchantColumn, diff --git a/src/pages/Travel/TripDetails.tsx b/src/pages/Travel/TripDetails.tsx index 8c3d1bbadfc1..dab130bde1aa 100644 --- a/src/pages/Travel/TripDetails.tsx +++ b/src/pages/Travel/TripDetails.tsx @@ -1,15 +1,41 @@ +import type {StackScreenProps} from '@react-navigation/stack'; import React from 'react'; import {NativeModules} from 'react-native'; +import {useOnyx} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import * as Expensicons from '@components/Icon/Expensicons'; +import MenuItem from '@components/MenuItem'; import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import usePermissions from '@hooks/usePermissions'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; +import type {TravelNavigatorParamList} from '@libs/Navigation/types'; +import * as ReportUtils from '@libs/ReportUtils'; +import * as Link from '@userActions/Link'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type SCREENS from '@src/SCREENS'; -function TripDetails() { +type TripDetailsProps = StackScreenProps; + +function TripDetails({route}: TripDetailsProps) { + const styles = useThemeStyles(); + const theme = useTheme(); const {translate} = useLocalize(); const {canUseSpotnanaTravel} = usePermissions(); + const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID); + const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${route.params.transactionID}`); + const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`); + const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID ?? '-1'}`); + const tripID = ReportUtils.getTripIDFromTransactionParentReportID(parentReport?.parentReportID); + + console.log(`transaction = `, transaction); + console.log(`report = `, report); + console.log(`parentReport = `, parentReport); + return ( + { + Link.openTravelDotLink(activePolicyID, CONST.TRIP_ID_PATH(tripID)); + }} + /> + { + Link.openTravelDotLink(activePolicyID, CONST.TRIP_ID_PATH(tripID)); + }} + /> ); From 610ed48107fc58b2dc04a569aaec214da5ab5ffc Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 25 Nov 2024 12:04:05 +0100 Subject: [PATCH 020/240] Add english translations --- src/languages/en.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/languages/en.ts b/src/languages/en.ts index ec0e49353bbb..e525ac717729 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2421,9 +2421,34 @@ const translations = { error: 'You must accept the Terms & Conditions for travel to continue', }, flight: 'Flight', + flightDetails: { + layover: 'layover', + takeOff: 'Take-off', + landing: 'Landing', + passenger: 'Passenger', + seat: 'Seat', + class: 'Cabin Class', + recordLocator: 'Record locator', + }, hotel: 'Hotel', + hotelDetails: { + checkIn: 'Check-in', + checkOut: 'Check-out', + roomType: 'Room type', + cancellation: 'Cancellation policy', + confirmation: 'Confirmation number', + }, car: 'Car', + carDetails: { + pickUp: 'Pick-up', + dropOff: 'Drop-off', + driver: 'Driver', + carType: 'Car type', + cancellation: 'Cancellation policy', + confirmation: 'Confirmation number', + }, viewTrip: 'View trip', + modifyTrip: 'Modify trip', tripSupport: 'Trip support', viewTripDetails: 'View trip details', trip: 'Trip', From 644243cc5815c134ed7624000e4103b38f94498f Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 25 Nov 2024 17:33:49 +0100 Subject: [PATCH 021/240] Make reservationType optional in getTripReservationIcon function --- src/libs/TripReservationUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/TripReservationUtils.ts b/src/libs/TripReservationUtils.ts index f2ce5113af81..9fd850ab161b 100644 --- a/src/libs/TripReservationUtils.ts +++ b/src/libs/TripReservationUtils.ts @@ -50,7 +50,7 @@ Onyx.connect({ }, }); -function getTripReservationIcon(reservationType: ReservationType): IconAsset { +function getTripReservationIcon(reservationType?: ReservationType): IconAsset { switch (reservationType) { case CONST.RESERVATION_TYPE.FLIGHT: return Expensicons.Plane; From d5dd2afc853a01393f909472368798583fcc06f4 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 25 Nov 2024 17:33:56 +0100 Subject: [PATCH 022/240] Add arrivalGate and cityName fields to Reservation and ReservationTimeDetails types --- src/types/onyx/Transaction.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 4c7e4facd94d..0183286510f6 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -236,6 +236,12 @@ type Reservation = { /** Payment type of the reservation */ paymentType?: string; + + /** Arrival gate details */ + arrivalGate?: { + /** Arrival terminal number */ + terminal: string; + }; }; /** Model of trip reservation time details */ @@ -257,6 +263,9 @@ type ReservationTimeDetails = { /** Timezone offset */ timezoneOffset?: string; + + /** City name */ + cityName?: string; }; /** Model of airline company details */ From 6c89f5a3f21c2723f18b6b1cab1fc664275c441b Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 25 Nov 2024 17:34:13 +0100 Subject: [PATCH 023/240] Enhance getFormattedTransportDate function to support shorter date format --- src/libs/DateUtils.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/libs/DateUtils.ts b/src/libs/DateUtils.ts index 2cab87639d2f..11482aec46b5 100644 --- a/src/libs/DateUtils.ts +++ b/src/libs/DateUtils.ts @@ -798,15 +798,19 @@ function getFormattedReservationRangeDate(date1: Date, date2: Date): string { /** * Returns a formatted date of departure. * Dates are formatted as follows: - * 1. When the date refers to the current day: Departs on Sunday, Mar 17 at 8:00 - * 2. When the date refers not to the current day: Departs on Wednesday, Mar 17, 2023 at 8:00 + * 1. When the date refers to the current year: Departs on Sunday, Mar 17 at 8:00. When shorter is true, the output is: Mar 17, 8:00 AM + * 2. When the date refers not to the current year: Departs on Wednesday, Mar 17, 2023 at 8:00. When shorter is true, the output is: Mar 17, 2023 8:00 AM */ -function getFormattedTransportDate(date: Date): string { +function getFormattedTransportDate(date: Date, shorter = false): string { const {translateLocal} = Localize; if (isThisYear(date)) { - return `${translateLocal('travel.departs')} ${format(date, 'EEEE, MMM d')} ${translateLocal('common.conjunctionAt')} ${format(date, 'HH:MM')}`; + return shorter + ? format(date, 'MMM d, h:mm a') + : `${translateLocal('travel.departs')} ${format(date, 'EEEE, MMM d')} ${translateLocal('common.conjunctionAt')} ${format(date, 'HH:MM')}`; } - return `${translateLocal('travel.departs')} ${format(date, 'EEEE, MMM d, yyyy')} ${translateLocal('common.conjunctionAt')} ${format(date, 'HH:MM')}`; + return shorter + ? format(date, 'MMM d, yyyy, h:mm a') + : `${translateLocal('travel.departs')} ${format(date, 'EEEE, MMM d, yyyy')} ${translateLocal('common.conjunctionAt')} ${format(date, 'HH:MM')}`; } function doesDateBelongToAPastYear(date: string): boolean { From b47f8a2f28ff5bb59b97ce18a0308976256e10e6 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 25 Nov 2024 17:34:21 +0100 Subject: [PATCH 024/240] Add FlightTripDetails component to display flight reservation information --- src/pages/Travel/FlightTripDetails.tsx | 132 +++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 src/pages/Travel/FlightTripDetails.tsx diff --git a/src/pages/Travel/FlightTripDetails.tsx b/src/pages/Travel/FlightTripDetails.tsx new file mode 100644 index 000000000000..6f32aeed0b42 --- /dev/null +++ b/src/pages/Travel/FlightTripDetails.tsx @@ -0,0 +1,132 @@ +import React from 'react'; +import {View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; +import * as Expensicons from '@components/Icon/Expensicons'; +import MenuItem from '@components/MenuItem'; +import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useStyleUtils from '@hooks/useStyleUtils'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; +import DateUtils from '@libs/DateUtils'; +import * as TripReservationUtils from '@libs/TripReservationUtils'; +import CONST from '@src/CONST'; +import type {PersonalDetails, Transaction} from '@src/types/onyx'; + +type FlightTripDetailsProps = { + transaction: OnyxEntry; + personalDetails: OnyxEntry; +}; + +function FlightTripDetails({transaction, personalDetails}: FlightTripDetailsProps) { + const styles = useThemeStyles(); + const theme = useTheme(); + const StyleUtils = useStyleUtils(); + const {translate} = useLocalize(); + + return ( + <> + + + + {transaction?.receipt?.reservationList?.map((reservation) => { + const reservationIcon = TripReservationUtils.getTripReservationIcon(reservation.type); + const startDate = DateUtils.getFormattedTransportDate(new Date(reservation.start.date), true); + const endDate = DateUtils.getFormattedTransportDate(new Date(reservation.end.date), true); + + return ( + <> + {translate('travel.flight')} + + {reservation.start.cityName} ({reservation.start.shortName}) {translate('common.conjunctionTo')} {reservation.end.cityName} ({reservation.end.shortName}) + + + + + + + + {!!reservation.route?.number && ( + + + + )} + {!!reservation.route?.class && ( + + + + )} + {!!reservation.confirmations?.at(0)?.value && ( + + + + )} + + + ); + })} + + ); +} + +FlightTripDetails.displayName = 'FlightTripDetails'; + +export default FlightTripDetails; From aeddf58eb767159b5184e6de188c4aefd9b3e152 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 25 Nov 2024 17:34:29 +0100 Subject: [PATCH 025/240] Integrate FlightTripDetails component into TripDetails screen and wrap content in ScrollView --- src/pages/Travel/TripDetails.tsx | 60 ++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/src/pages/Travel/TripDetails.tsx b/src/pages/Travel/TripDetails.tsx index dab130bde1aa..6c924d0957b6 100644 --- a/src/pages/Travel/TripDetails.tsx +++ b/src/pages/Travel/TripDetails.tsx @@ -7,21 +7,21 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; import ScreenWrapper from '@components/ScreenWrapper'; +import ScrollView from '@components/ScrollView'; import useLocalize from '@hooks/useLocalize'; import usePermissions from '@hooks/usePermissions'; import useTheme from '@hooks/useTheme'; -import useThemeStyles from '@hooks/useThemeStyles'; import type {TravelNavigatorParamList} from '@libs/Navigation/types'; import * as ReportUtils from '@libs/ReportUtils'; import * as Link from '@userActions/Link'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; +import FlightTripDetails from './FlightTripDetails'; type TripDetailsProps = StackScreenProps; function TripDetails({route}: TripDetailsProps) { - const styles = useThemeStyles(); const theme = useTheme(); const {translate} = useLocalize(); const {canUseSpotnanaTravel} = usePermissions(); @@ -30,11 +30,11 @@ function TripDetails({route}: TripDetailsProps) { const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${route.params.transactionID}`); const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`); const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID ?? '-1'}`); - const tripID = ReportUtils.getTripIDFromTransactionParentReportID(parentReport?.parentReportID); - console.log(`transaction = `, transaction); - console.log(`report = `, report); - console.log(`parentReport = `, parentReport); + const tripID = ReportUtils.getTripIDFromTransactionParentReportID(parentReport?.parentReportID); + const accountID = Object.keys(report?.participants ?? {}).at(0) ?? '-1'; + const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {selector: (data) => data?.[accountID]}); + const reservationType = transaction?.receipt?.reservationList?.at(0)?.type; return ( - { - Link.openTravelDotLink(activePolicyID, CONST.TRIP_ID_PATH(tripID)); - }} - /> - { - Link.openTravelDotLink(activePolicyID, CONST.TRIP_ID_PATH(tripID)); - }} - /> + + {reservationType === 'flight' && ( + + )} + { + Link.openTravelDotLink(activePolicyID, CONST.TRIP_ID_PATH(tripID)); + }} + /> + { + Link.openTravelDotLink(activePolicyID, CONST.TRIP_ID_PATH(tripID)); + }} + /> + ); From ae395901e50f5bf0ba1564941d1aab95a4e4d9d5 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Sun, 24 Nov 2024 20:39:28 +0100 Subject: [PATCH 026/240] Final touches --- src/components/ApprovalWorkflowSection.tsx | 34 +++++++++++++++++++--- src/components/AttachmentModal.tsx | 4 +-- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/components/ApprovalWorkflowSection.tsx b/src/components/ApprovalWorkflowSection.tsx index fd28595e7436..de6626c5bd5f 100644 --- a/src/components/ApprovalWorkflowSection.tsx +++ b/src/components/ApprovalWorkflowSection.tsx @@ -1,4 +1,5 @@ -import React, {useCallback, useMemo} from 'react'; +/* eslint-disable react/function-component-definition */ +import React, {useCallback, useMemo, useRef} from 'react'; import {View} from 'react-native'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; @@ -20,11 +21,36 @@ type ApprovalWorkflowSectionProps = { onPress: () => void; }; -function ApprovalWorkflowSection({approvalWorkflow, onPress}: ApprovalWorkflowSectionProps) { +const Test = () => { const styles = useThemeStyles(); const theme = useTheme(); + const {shouldUseNarrowLayout} = useResponsiveLayout(); const {translate, toLocaleOrdinal} = useLocalize(); + + const approverTitle = useCallback( + (index: number) => + approvalWorkflow.approvers.length > 1 ? `${toLocaleOrdinal(index + 1, true)} ${translate('workflowsPage.approver').toLowerCase()}` : `${translate('workflowsPage.approver')}`, + [approvalWorkflow.approvers.length, toLocaleOrdinal, translate], + ); + + const members = useMemo(() => { + if (approvalWorkflow.isDefault) { + return translate('workspace.common.everyone'); + } + + return OptionsListUtils.sortAlphabetically(approvalWorkflow.members, 'displayName') + .map((m) => m.displayName) + .join(', '); + }, [approvalWorkflow.isDefault, translate]); + + return ; +}; + +function ApprovalWorkflowSection({approvalWorkflow, onPress}: ApprovalWorkflowSectionProps) { + const styles = useThemeStyles(); + const theme = useTheme(); const {shouldUseNarrowLayout} = useResponsiveLayout(); + const {translate, toLocaleOrdinal} = useLocalize(); const approverTitle = useCallback( (index: number) => @@ -40,7 +66,7 @@ function ApprovalWorkflowSection({approvalWorkflow, onPress}: ApprovalWorkflowSe return OptionsListUtils.sortAlphabetically(approvalWorkflow.members, 'displayName') .map((m) => m.displayName) .join(', '); - }, [approvalWorkflow.isDefault, approvalWorkflow.members, translate]); + }, [approvalWorkflow]); return ( Date: Tue, 26 Nov 2024 09:31:52 +0100 Subject: [PATCH 027/240] Fix flight page after design changes --- src/libs/DateUtils.ts | 11 +++++ src/pages/Travel/FlightTripDetails.tsx | 60 +++++++++++++++----------- src/pages/Travel/TripDetails.tsx | 3 ++ 3 files changed, 48 insertions(+), 26 deletions(-) diff --git a/src/libs/DateUtils.ts b/src/libs/DateUtils.ts index 11482aec46b5..9bf707acdf46 100644 --- a/src/libs/DateUtils.ts +++ b/src/libs/DateUtils.ts @@ -13,6 +13,7 @@ import { formatDistance, getDate, getDay, + intervalToDuration, isAfter, isBefore, isSameDay, @@ -813,6 +814,15 @@ function getFormattedTransportDate(date: Date, shorter = false): string { : `${translateLocal('travel.departs')} ${format(date, 'EEEE, MMM d, yyyy')} ${translateLocal('common.conjunctionAt')} ${format(date, 'HH:MM')}`; } +/** + * Returns a formatted layover duration in format "2h 30m". + */ +function getFormattedDurationBetweenDates(start: Date, end: Date): string { + const {days, hours, minutes} = intervalToDuration({start, end}); + + return `${days ? `${days}d ` : ''}${hours ? `${hours}h ` : ''}${minutes}m`; +} + function doesDateBelongToAPastYear(date: string): boolean { const transactionYear = new Date(date).getFullYear(); return transactionYear !== new Date().getFullYear(); @@ -897,6 +907,7 @@ const DateUtils = { isCardExpired, getDifferenceInDaysFromNow, isValidDateString, + getFormattedDurationBetweenDates, }; export default DateUtils; diff --git a/src/pages/Travel/FlightTripDetails.tsx b/src/pages/Travel/FlightTripDetails.tsx index 6f32aeed0b42..b92c4ad57cce 100644 --- a/src/pages/Travel/FlightTripDetails.tsx +++ b/src/pages/Travel/FlightTripDetails.tsx @@ -28,29 +28,26 @@ function FlightTripDetails({transaction, personalDetails}: FlightTripDetailsProp return ( <> - - {transaction?.receipt?.reservationList?.map((reservation) => { + {transaction?.receipt?.reservationList?.map((reservation, index) => { const reservationIcon = TripReservationUtils.getTripReservationIcon(reservation.type); const startDate = DateUtils.getFormattedTransportDate(new Date(reservation.start.date), true); const endDate = DateUtils.getFormattedTransportDate(new Date(reservation.end.date), true); + const nextFlightStartDate = transaction.receipt?.reservationList?.at(index + 1)?.start.date; + const layover = nextFlightStartDate && DateUtils.getFormattedDurationBetweenDates(new Date(reservation.end.date), new Date(nextFlightStartDate)); + return ( <> - {translate('travel.flight')} + {translate('travel.flight')} {reservation.start.cityName} ({reservation.start.shortName}) {translate('common.conjunctionTo')} {reservation.end.cityName} ({reservation.end.shortName}) @@ -72,26 +69,18 @@ function FlightTripDetails({transaction, personalDetails}: FlightTripDetailsProp description={translate('travel.flightDetails.takeOff')} title={startDate} helperText={`${reservation.start.longName} (${reservation.start.shortName})${reservation.arrivalGate?.terminal ? `, ${reservation.arrivalGate?.terminal}` : ''}`} - helperTextStyle={styles.mtn2} + helperTextStyle={[styles.pb3, styles.mtn2]} interactive={false} /> - - - + + {!!reservation.route?.number && ( )} + + {!!layover && ( + <> + + + + )} ); })} diff --git a/src/pages/Travel/TripDetails.tsx b/src/pages/Travel/TripDetails.tsx index 6c924d0957b6..5eaee8d60dc4 100644 --- a/src/pages/Travel/TripDetails.tsx +++ b/src/pages/Travel/TripDetails.tsx @@ -11,6 +11,7 @@ import ScrollView from '@components/ScrollView'; import useLocalize from '@hooks/useLocalize'; import usePermissions from '@hooks/usePermissions'; import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; import type {TravelNavigatorParamList} from '@libs/Navigation/types'; import * as ReportUtils from '@libs/ReportUtils'; import * as Link from '@userActions/Link'; @@ -23,6 +24,7 @@ type TripDetailsProps = StackScreenProps { Link.openTravelDotLink(activePolicyID, CONST.TRIP_ID_PATH(tripID)); }} + wrapperStyle={styles.mt3} /> Date: Tue, 26 Nov 2024 09:33:48 +0100 Subject: [PATCH 028/240] Change TripDetails into TripDetailsPage --- .../AppNavigator/ModalStackNavigators/index.tsx | 2 +- .../Travel/{TripDetails.tsx => TripDetailsPage.tsx} | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) rename src/pages/Travel/{TripDetails.tsx => TripDetailsPage.tsx} (92%) diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index beb030ccb09d..543b0b374b8d 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -105,7 +105,7 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator({ [SCREENS.TRAVEL.MY_TRIPS]: () => require('../../../../pages/Travel/MyTripsPage').default, [SCREENS.TRAVEL.TCS]: () => require('../../../../pages/Travel/TravelTerms').default, - [SCREENS.TRAVEL.TRIP_DETAILS]: () => require('../../../../pages/Travel/TripDetails').default, + [SCREENS.TRAVEL.TRIP_DETAILS]: () => require('../../../../pages/Travel/TripDetailsPage').default, }); const SplitDetailsModalStackNavigator = createModalStackNavigator({ diff --git a/src/pages/Travel/TripDetails.tsx b/src/pages/Travel/TripDetailsPage.tsx similarity index 92% rename from src/pages/Travel/TripDetails.tsx rename to src/pages/Travel/TripDetailsPage.tsx index 5eaee8d60dc4..0f35bc0c36e1 100644 --- a/src/pages/Travel/TripDetails.tsx +++ b/src/pages/Travel/TripDetailsPage.tsx @@ -20,9 +20,9 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import FlightTripDetails from './FlightTripDetails'; -type TripDetailsProps = StackScreenProps; +type TripDetailsPageProps = StackScreenProps; -function TripDetails({route}: TripDetailsProps) { +function TripDetailsPage({route}: TripDetailsPageProps) { const theme = useTheme(); const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -43,7 +43,7 @@ function TripDetails({route}: TripDetailsProps) { includeSafeAreaPaddingBottom={false} shouldEnablePickerAvoiding={false} shouldEnableMaxHeight - testID={TripDetails.displayName} + testID={TripDetailsPage.displayName} shouldShowOfflineIndicatorInWideScreen > Date: Tue, 26 Nov 2024 10:16:35 +0100 Subject: [PATCH 029/240] Create HotelTripDetails --- src/pages/Travel/HotelTripDetails.tsx | 88 +++++++++++++++++++++++++++ src/pages/Travel/TripDetailsPage.tsx | 7 +++ 2 files changed, 95 insertions(+) create mode 100644 src/pages/Travel/HotelTripDetails.tsx diff --git a/src/pages/Travel/HotelTripDetails.tsx b/src/pages/Travel/HotelTripDetails.tsx new file mode 100644 index 000000000000..f0f8ec7398cc --- /dev/null +++ b/src/pages/Travel/HotelTripDetails.tsx @@ -0,0 +1,88 @@ +import React from 'react'; +import {View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; +import * as Expensicons from '@components/Icon/Expensicons'; +import MenuItem from '@components/MenuItem'; +import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useStyleUtils from '@hooks/useStyleUtils'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; +import DateUtils from '@libs/DateUtils'; +import * as TripReservationUtils from '@libs/TripReservationUtils'; +import CONST from '@src/CONST'; +import type {PersonalDetails, Transaction} from '@src/types/onyx'; + +type HotelTripDetailsProps = { + transaction: OnyxEntry; + personalDetails: OnyxEntry; +}; + +function HotelTripDetails({transaction, personalDetails}: HotelTripDetailsProps) { + const styles = useThemeStyles(); + const theme = useTheme(); + const StyleUtils = useStyleUtils(); + const {translate} = useLocalize(); + + const hotelReservation = transaction?.receipt?.reservationList?.at(0); + + if (!transaction || !hotelReservation) { + return null; + } + const reservationIcon = TripReservationUtils.getTripReservationIcon(hotelReservation.type); + const checkInDate = DateUtils.getFormattedTransportDate(new Date(hotelReservation.start.date), true); + const checkOutDate = DateUtils.getFormattedTransportDate(new Date(hotelReservation.end.date), true); + + return ( + <> + + {translate('travel.hotel')} + {hotelReservation.start.longName} + + + + {!!hotelReservation.confirmations?.at(0)?.value && ( + + + + )} + + ); +} + +HotelTripDetails.displayName = 'FlightTripDetails'; + +export default HotelTripDetails; diff --git a/src/pages/Travel/TripDetailsPage.tsx b/src/pages/Travel/TripDetailsPage.tsx index 0f35bc0c36e1..590ab2949ea5 100644 --- a/src/pages/Travel/TripDetailsPage.tsx +++ b/src/pages/Travel/TripDetailsPage.tsx @@ -19,6 +19,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import FlightTripDetails from './FlightTripDetails'; +import HotelTripDetails from './HotelTripDetails'; type TripDetailsPageProps = StackScreenProps; @@ -61,6 +62,12 @@ function TripDetailsPage({route}: TripDetailsPageProps) { personalDetails={personalDetails} /> )} + {reservationType === 'hotel' && ( + + )} Date: Tue, 26 Nov 2024 10:16:43 +0100 Subject: [PATCH 030/240] Add missing translation --- src/languages/en.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/languages/en.ts b/src/languages/en.ts index e525ac717729..81d2a9dfc5c9 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2432,6 +2432,7 @@ const translations = { }, hotel: 'Hotel', hotelDetails: { + guest: 'Guest', checkIn: 'Check-in', checkOut: 'Check-out', roomType: 'Room type', From 2eb1bb96d8d633b605c75a0d9e059cac1ff4b4f8 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 26 Nov 2024 11:48:40 +0100 Subject: [PATCH 031/240] Revert "Final touches" This reverts commit 04d94db6fc38eb5cd30e85373c3ac58ca28ebc26. --- src/components/ApprovalWorkflowSection.tsx | 34 +++------------------- src/components/AttachmentModal.tsx | 4 ++- 2 files changed, 7 insertions(+), 31 deletions(-) diff --git a/src/components/ApprovalWorkflowSection.tsx b/src/components/ApprovalWorkflowSection.tsx index de6626c5bd5f..fd28595e7436 100644 --- a/src/components/ApprovalWorkflowSection.tsx +++ b/src/components/ApprovalWorkflowSection.tsx @@ -1,5 +1,4 @@ -/* eslint-disable react/function-component-definition */ -import React, {useCallback, useMemo, useRef} from 'react'; +import React, {useCallback, useMemo} from 'react'; import {View} from 'react-native'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; @@ -21,36 +20,11 @@ type ApprovalWorkflowSectionProps = { onPress: () => void; }; -const Test = () => { - const styles = useThemeStyles(); - const theme = useTheme(); - const {shouldUseNarrowLayout} = useResponsiveLayout(); - const {translate, toLocaleOrdinal} = useLocalize(); - - const approverTitle = useCallback( - (index: number) => - approvalWorkflow.approvers.length > 1 ? `${toLocaleOrdinal(index + 1, true)} ${translate('workflowsPage.approver').toLowerCase()}` : `${translate('workflowsPage.approver')}`, - [approvalWorkflow.approvers.length, toLocaleOrdinal, translate], - ); - - const members = useMemo(() => { - if (approvalWorkflow.isDefault) { - return translate('workspace.common.everyone'); - } - - return OptionsListUtils.sortAlphabetically(approvalWorkflow.members, 'displayName') - .map((m) => m.displayName) - .join(', '); - }, [approvalWorkflow.isDefault, translate]); - - return ; -}; - function ApprovalWorkflowSection({approvalWorkflow, onPress}: ApprovalWorkflowSectionProps) { const styles = useThemeStyles(); const theme = useTheme(); - const {shouldUseNarrowLayout} = useResponsiveLayout(); const {translate, toLocaleOrdinal} = useLocalize(); + const {shouldUseNarrowLayout} = useResponsiveLayout(); const approverTitle = useCallback( (index: number) => @@ -66,7 +40,7 @@ function ApprovalWorkflowSection({approvalWorkflow, onPress}: ApprovalWorkflowSe return OptionsListUtils.sortAlphabetically(approvalWorkflow.members, 'displayName') .map((m) => m.displayName) .join(', '); - }, [approvalWorkflow]); + }, [approvalWorkflow.isDefault, approvalWorkflow.members, translate]); return ( Date: Tue, 26 Nov 2024 11:52:58 +0100 Subject: [PATCH 032/240] Remove flight from hotel page --- src/pages/Travel/HotelTripDetails.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/Travel/HotelTripDetails.tsx b/src/pages/Travel/HotelTripDetails.tsx index f0f8ec7398cc..b77b3530fca9 100644 --- a/src/pages/Travel/HotelTripDetails.tsx +++ b/src/pages/Travel/HotelTripDetails.tsx @@ -73,7 +73,7 @@ function HotelTripDetails({transaction, personalDetails}: HotelTripDetailsProps) {!!hotelReservation.confirmations?.at(0)?.value && ( @@ -83,6 +83,6 @@ function HotelTripDetails({transaction, personalDetails}: HotelTripDetailsProps) ); } -HotelTripDetails.displayName = 'FlightTripDetails'; +HotelTripDetails.displayName = 'HotelTripDetails'; export default HotelTripDetails; From a4a0cded4bec256a13dd85c03ffdbf176319e618 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 26 Nov 2024 15:42:19 +0100 Subject: [PATCH 033/240] Add a blank line for improved readability in HotelTripDetails component --- src/pages/Travel/HotelTripDetails.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/Travel/HotelTripDetails.tsx b/src/pages/Travel/HotelTripDetails.tsx index b77b3530fca9..32f0a6836828 100644 --- a/src/pages/Travel/HotelTripDetails.tsx +++ b/src/pages/Travel/HotelTripDetails.tsx @@ -30,6 +30,7 @@ function HotelTripDetails({transaction, personalDetails}: HotelTripDetailsProps) if (!transaction || !hotelReservation) { return null; } + const reservationIcon = TripReservationUtils.getTripReservationIcon(hotelReservation.type); const checkInDate = DateUtils.getFormattedTransportDate(new Date(hotelReservation.start.date), true); const checkOutDate = DateUtils.getFormattedTransportDate(new Date(hotelReservation.end.date), true); From 30e27d1c2ba817f810c4f2e4a2b8c57a1346eb48 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 26 Nov 2024 15:42:26 +0100 Subject: [PATCH 034/240] Add CarTripDetails component for displaying car rental information --- src/pages/Travel/CarTripDetails.tsx | 103 ++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 src/pages/Travel/CarTripDetails.tsx diff --git a/src/pages/Travel/CarTripDetails.tsx b/src/pages/Travel/CarTripDetails.tsx new file mode 100644 index 000000000000..25c417425a9f --- /dev/null +++ b/src/pages/Travel/CarTripDetails.tsx @@ -0,0 +1,103 @@ +import React from 'react'; +import {View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; +import * as Expensicons from '@components/Icon/Expensicons'; +import MenuItem from '@components/MenuItem'; +import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useStyleUtils from '@hooks/useStyleUtils'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; +import DateUtils from '@libs/DateUtils'; +import * as TripReservationUtils from '@libs/TripReservationUtils'; +import CONST from '@src/CONST'; +import type {PersonalDetails, Transaction} from '@src/types/onyx'; + +type CarTripDetailsProps = { + transaction: OnyxEntry; + personalDetails: OnyxEntry; +}; + +function CarTripDetails({transaction, personalDetails}: CarTripDetailsProps) { + const styles = useThemeStyles(); + const theme = useTheme(); + const StyleUtils = useStyleUtils(); + const {translate} = useLocalize(); + + const carReservation = transaction?.receipt?.reservationList?.at(0); + + if (!transaction || !carReservation) { + return null; + } + + const reservationIcon = TripReservationUtils.getTripReservationIcon(carReservation.type); + const checkInDate = DateUtils.getFormattedTransportDate(new Date(carReservation.start.date), true); + const checkOutDate = DateUtils.getFormattedTransportDate(new Date(carReservation.end.date), true); + + return ( + <> + + {translate('travel.carDetails.rentalCar')} + {carReservation.vendor} + + + + + {!!carReservation.carInfo?.name && ( + + + + )} + {!!carReservation.reservationID && ( + + + + )} + + ); +} + +CarTripDetails.displayName = 'FlightTripDetails'; + +export default CarTripDetails; From a8b78f875075575e7400e32e8137c95bf5b1957b Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 26 Nov 2024 15:42:37 +0100 Subject: [PATCH 035/240] Refactor getReservationsFromTripTransactions to return structured reservation data and sort by start date --- src/libs/TripReservationUtils.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/libs/TripReservationUtils.ts b/src/libs/TripReservationUtils.ts index 9fd850ab161b..a8714122da97 100644 --- a/src/libs/TripReservationUtils.ts +++ b/src/libs/TripReservationUtils.ts @@ -63,11 +63,19 @@ function getTripReservationIcon(reservationType?: ReservationType): IconAsset { } } -function getReservationsFromTripTransactions(transactions: Transaction[]): Reservation[] { +type ReservationData = {reservation: Reservation; transactionID: string; reportID: string}; + +function getReservationsFromTripTransactions(transactions: Transaction[]): ReservationData[] { return transactions - .map((item) => item?.receipt?.reservationList ?? []) - .filter((item) => item.length > 0) - .flat(); + .flatMap( + (item) => + item?.receipt?.reservationList?.map((reservation) => ({ + reservation, + transactionID: item.transactionID, + reportID: item.reportID, + })) ?? [], + ) + .sort((a, b) => new Date(a.reservation.start.date).getTime() - new Date(b.reservation.start.date).getTime()); } function getTripEReceiptIcon(transaction?: Transaction): IconAsset | undefined { @@ -115,3 +123,4 @@ function bookATrip(translate: LocaleContextProps['translate'], setCtaErrorMessag }); } export {getTripReservationIcon, getReservationsFromTripTransactions, getTripEReceiptIcon, bookATrip}; +export type {ReservationData}; From f4f5501fe0c7afb0d4ee4c819643c40e9ce2ae79 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 26 Nov 2024 15:42:47 +0100 Subject: [PATCH 036/240] Add translation for rental car --- src/languages/en.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/languages/en.ts b/src/languages/en.ts index 81d2a9dfc5c9..e1b932a3852a 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2441,6 +2441,7 @@ const translations = { }, car: 'Car', carDetails: { + rentalCar: 'Rental car', pickUp: 'Pick-up', dropOff: 'Drop-off', driver: 'Driver', From d49cdce25f3839547511714ccaf716499374616d Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 26 Nov 2024 15:43:01 +0100 Subject: [PATCH 037/240] Fix navigation route in MoneyRequestView to use parentReportID --- src/components/ReportActionItem/MoneyRequestView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 6bc356dd6f15..4fb50ad39cc1 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -695,7 +695,7 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals title={translate('travel.viewTripDetails')} icon={Expensicons.Suitcase} onPress={() => - Navigation.navigate(ROUTES.TRAVEL_TRIP_DETAILS.getRoute(report?.reportID ?? '-1', transaction?.transactionID ?? '-1', Navigation.getReportRHPActiveRoute())) + Navigation.navigate(ROUTES.TRAVEL_TRIP_DETAILS.getRoute(report?.parentReportID ?? '-1', transaction?.transactionID ?? '-1', Navigation.getReportRHPActiveRoute())) } /> )} From 092e430260226f367a28150edb89477e8cca435c Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 26 Nov 2024 15:43:29 +0100 Subject: [PATCH 038/240] Refactor TripDetailsView and TripRoomPreview to use structured reservation data and improve navigation handling --- .../ReportActionItem/TripDetailsView.tsx | 25 +++++++++++---- .../ReportActionItem/TripRoomPreview.tsx | 32 +++++++------------ src/libs/ReportUtils.ts | 6 ++-- 3 files changed, 33 insertions(+), 30 deletions(-) diff --git a/src/components/ReportActionItem/TripDetailsView.tsx b/src/components/ReportActionItem/TripDetailsView.tsx index 32cbc5dd853e..0043232d1119 100644 --- a/src/components/ReportActionItem/TripDetailsView.tsx +++ b/src/components/ReportActionItem/TripDetailsView.tsx @@ -11,11 +11,13 @@ import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import DateUtils from '@libs/DateUtils'; +import Navigation from '@libs/Navigation/Navigation'; import variables from '@styles/variables'; import * as Expensicons from '@src/components/Icon/Expensicons'; import CONST from '@src/CONST'; import * as ReportUtils from '@src/libs/ReportUtils'; import * as TripReservationUtils from '@src/libs/TripReservationUtils'; +import ROUTES from '@src/ROUTES'; import type {Reservation, ReservationTimeDetails} from '@src/types/onyx/Transaction'; type TripDetailsViewProps = { @@ -28,9 +30,11 @@ type TripDetailsViewProps = { type ReservationViewProps = { reservation: Reservation; + transactionID: string; + reportID: string; }; -function ReservationView({reservation}: ReservationViewProps) { +function ReservationView({reservation, transactionID, reportID}: ReservationViewProps) { const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -129,6 +133,7 @@ function ReservationView({reservation}: ReservationViewProps) { iconWidth={20} iconStyles={[StyleUtils.getTripReservationIconContainer(false), styles.mr3]} secondaryIconFill={theme.icon} + onPress={() => Navigation.navigate(ROUTES.TRAVEL_TRIP_DETAILS.getRoute(reportID, transactionID, Navigation.getReportRHPActiveRoute()))} /> ); } @@ -138,7 +143,7 @@ function TripDetailsView({tripRoomReportID, shouldShowHorizontalRule}: TripDetai const {translate} = useLocalize(); const tripTransactions = ReportUtils.getTripTransactions(tripRoomReportID); - const reservations: Reservation[] = TripReservationUtils.getReservationsFromTripTransactions(tripTransactions); + const reservationsData: TripReservationUtils.ReservationData[] = TripReservationUtils.getReservationsFromTripTransactions(tripTransactions); return ( @@ -153,11 +158,17 @@ function TripDetailsView({tripRoomReportID, shouldShowHorizontalRule}: TripDetai <> - {reservations.map((reservation) => ( - - - - ))} + {reservationsData.map(({reservation, transactionID, reportID}) => { + return ( + + + + ); + })} ; +const renderItem = ({item}: {item: TripReservationUtils.ReservationData}) => ; function TripRoomPreview({action, chatReportID, containerStyles, contextMenuAnchor, isHovered = false, checkIfContextMenuActive = () => {}}: TripRoomPreviewProps) { const styles = useThemeStyles(); @@ -112,31 +111,22 @@ function TripRoomPreview({action, chatReportID, containerStyles, contextMenuAnch const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`); const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReport?.iouReportID}`); - const tripTransactions = ReportUtils.getTripTransactions(chatReport?.iouReportID, 'reportID'); - const reservations: Reservation[] = TripReservationUtils.getReservationsFromTripTransactions(tripTransactions); + const tripTransactions = ReportUtils.getTripTransactions(chatReport?.reportID); + const reservationsData: TripReservationUtils.ReservationData[] = TripReservationUtils.getReservationsFromTripTransactions(tripTransactions); const dateInfo = chatReport?.tripData ? DateUtils.getFormattedDateRange(new Date(chatReport.tripData.startDate), new Date(chatReport.tripData.endDate)) : ''; const {totalDisplaySpend} = ReportUtils.getMoneyRequestSpendBreakdown(chatReport); + const currency = iouReport?.currency ?? chatReport?.currency; const displayAmount = useMemo(() => { if (totalDisplaySpend) { - return CurrencyUtils.convertToDisplayString(totalDisplaySpend, iouReport?.currency); + return CurrencyUtils.convertToDisplayString(totalDisplaySpend, currency); } - // If iouReport is not available, get amount from the action message (Ex: "Domain20821's Workspace owes $33.00" or "paid ₫60" or "paid -₫60 elsewhere") - let displayAmountValue = ''; - const actionMessage = getReportActionText(action) ?? ''; - const splits = actionMessage.split(' '); - - splits.forEach((split) => { - if (!/\d/.test(split)) { - return; - } - - displayAmountValue = split; - }); - - return displayAmountValue; - }, [action, iouReport?.currency, totalDisplaySpend]); + return CurrencyUtils.convertToDisplayString( + tripTransactions.reduce((acc, transaction) => acc + Math.abs(transaction.amount), 0), + currency, + ); + }, [currency, totalDisplaySpend, tripTransactions]); return ( diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 72f11f4a3150..534559dd8cb8 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -854,7 +854,7 @@ function isChatReport(report: OnyxEntry): boolean { return report?.type === CONST.REPORT.TYPE.CHAT; } -function isInvoiceReport(report: OnyxInputOrEntry | SearchReport): boolean { +function isInvoiceReport(report: OnyxInputOrEntry | SearchReport): report is Report { return report?.type === CONST.REPORT.TYPE.INVOICE; } @@ -1659,6 +1659,8 @@ function isMoneyRequest(reportOrID: OnyxEntry | string): boolean { /** * Checks if a report is an IOU or expense report. */ +function isMoneyRequestReport(reportOrID: OnyxInputOrEntry): reportOrID is Report; +function isMoneyRequestReport(reportOrID: SearchReport | string): boolean; function isMoneyRequestReport(reportOrID: OnyxInputOrEntry | SearchReport | string): boolean { const report = typeof reportOrID === 'string' ? ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] ?? null : reportOrID; return isIOUReport(report) || isExpenseReport(report); @@ -2837,7 +2839,7 @@ function hasNonReimbursableTransactions(iouReportID: string | undefined): boolea function getMoneyRequestSpendBreakdown(report: OnyxInputOrEntry, allReportsDict?: OnyxCollection): SpendBreakdown { const allAvailableReports = allReportsDict ?? ReportConnection.getAllReports(); - let moneyRequestReport; + let moneyRequestReport: OnyxEntry; if (isMoneyRequestReport(report) || isInvoiceReport(report)) { moneyRequestReport = report; } From 6c59ca4a4208e2d946c75be3357c551ecb4fe5a9 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 26 Nov 2024 15:43:39 +0100 Subject: [PATCH 039/240] Add CarTripDetails component to TripDetailsPage for displaying car rental information --- src/pages/Travel/TripDetailsPage.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/pages/Travel/TripDetailsPage.tsx b/src/pages/Travel/TripDetailsPage.tsx index 590ab2949ea5..971128101486 100644 --- a/src/pages/Travel/TripDetailsPage.tsx +++ b/src/pages/Travel/TripDetailsPage.tsx @@ -18,6 +18,7 @@ import * as Link from '@userActions/Link'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; +import CarTripDetails from './CarTripDetails'; import FlightTripDetails from './FlightTripDetails'; import HotelTripDetails from './HotelTripDetails'; @@ -32,9 +33,8 @@ function TripDetailsPage({route}: TripDetailsPageProps) { const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID); const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${route.params.transactionID}`); const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`); - const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID ?? '-1'}`); - const tripID = ReportUtils.getTripIDFromTransactionParentReportID(parentReport?.parentReportID); + const tripID = ReportUtils.getTripIDFromTransactionParentReportID(report?.parentReportID); const accountID = Object.keys(report?.participants ?? {}).at(0) ?? '-1'; const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {selector: (data) => data?.[accountID]}); const reservationType = transaction?.receipt?.reservationList?.at(0)?.type; @@ -68,6 +68,12 @@ function TripDetailsPage({route}: TripDetailsPageProps) { personalDetails={personalDetails} /> )} + {reservationType === 'car' && ( + + )} Date: Wed, 27 Nov 2024 18:29:42 +0100 Subject: [PATCH 040/240] Add Trip Summary screen --- src/ROUTES.ts | 7 +- src/SCREENS.ts | 1 + .../ReportActionItem/TripDetailsView.tsx | 9 ++- src/pages/Travel/TripSummary.tsx | 65 +++++++++++++++++++ 4 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 src/pages/Travel/TripSummary.tsx diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 920e328ebfff..2be153e4dad3 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -1343,10 +1343,15 @@ const ROUTES = { TRAVEL_MY_TRIPS: 'travel', TRAVEL_TCS: 'travel/terms', TRACK_TRAINING_MODAL: 'track-training', - TRAVEL_TRIP_DETAILS: { + TRAVEL_TRIP_SUMMARY: { route: 'r/:reportID/trip/:transactionID', getRoute: (reportID: string, transactionID: string, backTo?: string) => getUrlWithBackToParam(`r/${reportID}/trip/${transactionID}`, backTo), }, + TRAVEL_TRIP_DETAILS: { + route: 'r/:reportID/trip/:transactionID/:reservationIndex', + getRoute: (reportID: string, transactionID: string, reservationIndex: number, backTo?: string) => + getUrlWithBackToParam(`r/${reportID}/trip/${transactionID}/${reservationIndex}`, backTo), + }, ONBOARDING_ROOT: { route: 'onboarding', getRoute: (backTo?: string) => getUrlWithBackToParam(`onboarding`, backTo), diff --git a/src/SCREENS.ts b/src/SCREENS.ts index f6a53da3f199..eeb805d0ca36 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -27,6 +27,7 @@ const SCREENS = { TRAVEL: { MY_TRIPS: 'Travel_MyTrips', TCS: 'Travel_TCS', + TRIP_SUMMARY: 'Travel_TripSummary', TRIP_DETAILS: 'Trip_Details', }, SEARCH: { diff --git a/src/components/ReportActionItem/TripDetailsView.tsx b/src/components/ReportActionItem/TripDetailsView.tsx index 0043232d1119..dfc282b230e4 100644 --- a/src/components/ReportActionItem/TripDetailsView.tsx +++ b/src/components/ReportActionItem/TripDetailsView.tsx @@ -32,9 +32,10 @@ type ReservationViewProps = { reservation: Reservation; transactionID: string; reportID: string; + reservationIndex: number; }; -function ReservationView({reservation, transactionID, reportID}: ReservationViewProps) { +function ReservationView({reservation, transactionID, reportID, reservationIndex}: ReservationViewProps) { const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -133,7 +134,7 @@ function ReservationView({reservation, transactionID, reportID}: ReservationView iconWidth={20} iconStyles={[StyleUtils.getTripReservationIconContainer(false), styles.mr3]} secondaryIconFill={theme.icon} - onPress={() => Navigation.navigate(ROUTES.TRAVEL_TRIP_DETAILS.getRoute(reportID, transactionID, Navigation.getReportRHPActiveRoute()))} + onPress={() => Navigation.navigate(ROUTES.TRAVEL_TRIP_DETAILS.getRoute(reportID, transactionID, reservationIndex, Navigation.getReportRHPActiveRoute()))} /> ); } @@ -158,13 +159,14 @@ function TripDetailsView({tripRoomReportID, shouldShowHorizontalRule}: TripDetai <> - {reservationsData.map(({reservation, transactionID, reportID}) => { + {reservationsData.map(({reservation, transactionID, reportID, reservationIndex}) => { return ( ); @@ -181,3 +183,4 @@ function TripDetailsView({tripRoomReportID, shouldShowHorizontalRule}: TripDetai TripDetailsView.displayName = 'TripDetailsView'; export default TripDetailsView; +export {ReservationView}; diff --git a/src/pages/Travel/TripSummary.tsx b/src/pages/Travel/TripSummary.tsx new file mode 100644 index 000000000000..a024d28bd6db --- /dev/null +++ b/src/pages/Travel/TripSummary.tsx @@ -0,0 +1,65 @@ +import type {StackScreenProps} from '@react-navigation/stack'; +import React from 'react'; +import {NativeModules} from 'react-native'; +import {useOnyx} from 'react-native-onyx'; +import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import OfflineWithFeedback from '@components/OfflineWithFeedback'; +import {ReservationView} from '@components/ReportActionItem/TripDetailsView'; +import ScreenWrapper from '@components/ScreenWrapper'; +import ScrollView from '@components/ScrollView'; +import useLocalize from '@hooks/useLocalize'; +import usePermissions from '@hooks/usePermissions'; +import type {TravelNavigatorParamList} from '@libs/Navigation/types'; +import * as TripReservationUtils from '@src/libs/TripReservationUtils'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type SCREENS from '@src/SCREENS'; + +type TripDetailsPageProps = StackScreenProps; + +function TripDetailsPage({route}: TripDetailsPageProps) { + const {translate} = useLocalize(); + const {canUseSpotnanaTravel} = usePermissions(); + + const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${route.params.transactionID}`); + const reservationType = transaction?.receipt?.reservationList?.at(0)?.type; + const reservationsData: TripReservationUtils.ReservationData[] = TripReservationUtils.getReservationsFromTripTransactions(transaction ? [transaction] : []); + + return ( + + + + + {reservationsData.map(({reservation, transactionID, reportID, reservationIndex}) => { + return ( + + + + ); + })} + + + + ); +} + +TripDetailsPage.displayName = 'TripDetailsPage'; + +export default TripDetailsPage; From 9c5963e50f51d541e70f171454947e9e440c6d22 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 27 Nov 2024 18:30:02 +0100 Subject: [PATCH 041/240] Add address translation to common --- src/languages/en.ts | 3 ++- src/languages/es.ts | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index e1b932a3852a..f745d2d3f579 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -474,6 +474,7 @@ const translations = { links: 'Links', days: 'days', rename: 'Rename', + address: 'Address', }, location: { useCurrent: 'Use current location', @@ -2441,7 +2442,7 @@ const translations = { }, car: 'Car', carDetails: { - rentalCar: 'Rental car', + rentalCar: 'Car rental', pickUp: 'Pick-up', dropOff: 'Drop-off', driver: 'Driver', diff --git a/src/languages/es.ts b/src/languages/es.ts index 623afb1ff16a..edcba33bd58a 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -465,6 +465,7 @@ const translations = { sent: 'Enviado', links: 'Enlaces', days: 'días', + address: 'Dirección', }, connectionComplete: { title: 'Conexión completa', From 34553ef3aeb1a09e42257e9703f15f1b463c2cf3 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 27 Nov 2024 18:30:50 +0100 Subject: [PATCH 042/240] Add screen to navigator and fix types --- .../AppNavigator/ModalStackNavigators/index.tsx | 1 + src/libs/Navigation/types.ts | 6 ++++++ .../Travel/{TripSummary.tsx => TripSummaryPage.tsx} | 10 +++++----- 3 files changed, 12 insertions(+), 5 deletions(-) rename src/pages/Travel/{TripSummary.tsx => TripSummaryPage.tsx} (90%) diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 543b0b374b8d..377013af0ada 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -105,6 +105,7 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator({ [SCREENS.TRAVEL.MY_TRIPS]: () => require('../../../../pages/Travel/MyTripsPage').default, [SCREENS.TRAVEL.TCS]: () => require('../../../../pages/Travel/TravelTerms').default, + [SCREENS.TRAVEL.TRIP_SUMMARY]: () => require('../../../../pages/Travel/TripSummaryPage').default, [SCREENS.TRAVEL.TRIP_DETAILS]: () => require('../../../../pages/Travel/TripDetailsPage').default, }); diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 469d20d15bbc..750542a55172 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1380,9 +1380,15 @@ type RightModalNavigatorParamList = { type TravelNavigatorParamList = { [SCREENS.TRAVEL.MY_TRIPS]: undefined; + [SCREENS.TRAVEL.TRIP_SUMMARY]: { + reportID: string; + transactionID: string; + backTo?: string; + }; [SCREENS.TRAVEL.TRIP_DETAILS]: { reportID: string; transactionID: string; + reservationIndex: number; backTo?: string; }; }; diff --git a/src/pages/Travel/TripSummary.tsx b/src/pages/Travel/TripSummaryPage.tsx similarity index 90% rename from src/pages/Travel/TripSummary.tsx rename to src/pages/Travel/TripSummaryPage.tsx index a024d28bd6db..92a9aab8e107 100644 --- a/src/pages/Travel/TripSummary.tsx +++ b/src/pages/Travel/TripSummaryPage.tsx @@ -15,9 +15,9 @@ import * as TripReservationUtils from '@src/libs/TripReservationUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; -type TripDetailsPageProps = StackScreenProps; +type TripSummaryPageProps = StackScreenProps; -function TripDetailsPage({route}: TripDetailsPageProps) { +function TripSummaryPage({route}: TripSummaryPageProps) { const {translate} = useLocalize(); const {canUseSpotnanaTravel} = usePermissions(); @@ -30,7 +30,7 @@ function TripDetailsPage({route}: TripDetailsPageProps) { includeSafeAreaPaddingBottom={false} shouldEnablePickerAvoiding={false} shouldEnableMaxHeight - testID={TripDetailsPage.displayName} + testID={TripSummaryPage.displayName} shouldShowOfflineIndicatorInWideScreen > Date: Wed, 27 Nov 2024 18:32:54 +0100 Subject: [PATCH 043/240] Navigate to summary conditionally --- .../ReportActionItem/MoneyRequestView.tsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 4fb50ad39cc1..56fdf582f5b7 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -694,9 +694,17 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals - Navigation.navigate(ROUTES.TRAVEL_TRIP_DETAILS.getRoute(report?.parentReportID ?? '-1', transaction?.transactionID ?? '-1', Navigation.getReportRHPActiveRoute())) - } + onPress={() => { + const reservations = transaction?.receipt?.reservationList?.length ?? 0; + if (reservations > 1) { + Navigation.navigate( + ROUTES.TRAVEL_TRIP_SUMMARY.getRoute(report?.parentReportID ?? '-1', transaction?.transactionID ?? '-1', Navigation.getReportRHPActiveRoute()), + ); + } + Navigation.navigate( + ROUTES.TRAVEL_TRIP_DETAILS.getRoute(report?.parentReportID ?? '-1', transaction?.transactionID ?? '-1', 0, Navigation.getReportRHPActiveRoute()), + ); + }} /> )} {shouldShowAttendees && ( From a1ebfe7db4c327d6d9d8ff755976fb526ab0ac1f Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 27 Nov 2024 18:33:13 +0100 Subject: [PATCH 044/240] Clean date utils around transport --- src/libs/DateUtils.ts | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/libs/DateUtils.ts b/src/libs/DateUtils.ts index 9bf707acdf46..21c0a2b7e375 100644 --- a/src/libs/DateUtils.ts +++ b/src/libs/DateUtils.ts @@ -799,19 +799,34 @@ function getFormattedReservationRangeDate(date1: Date, date2: Date): string { /** * Returns a formatted date of departure. * Dates are formatted as follows: - * 1. When the date refers to the current year: Departs on Sunday, Mar 17 at 8:00. When shorter is true, the output is: Mar 17, 8:00 AM - * 2. When the date refers not to the current year: Departs on Wednesday, Mar 17, 2023 at 8:00. When shorter is true, the output is: Mar 17, 2023 8:00 AM + * 1. When the date refers to the current year: Departs on Sunday, Mar 17 at 8:00. + * 2. When the date refers not to the current year: Departs on Wednesday, Mar 17, 2023 at 8:00. */ -function getFormattedTransportDate(date: Date, shorter = false): string { +function getFormattedTransportDate(date: Date): string { const {translateLocal} = Localize; if (isThisYear(date)) { - return shorter - ? format(date, 'MMM d, h:mm a') - : `${translateLocal('travel.departs')} ${format(date, 'EEEE, MMM d')} ${translateLocal('common.conjunctionAt')} ${format(date, 'HH:MM')}`; + return `${translateLocal('travel.departs')} ${format(date, 'EEEE, MMM d')} ${translateLocal('common.conjunctionAt')} ${format(date, 'HH:MM')}`; } - return shorter - ? format(date, 'MMM d, yyyy, h:mm a') - : `${translateLocal('travel.departs')} ${format(date, 'EEEE, MMM d, yyyy')} ${translateLocal('common.conjunctionAt')} ${format(date, 'HH:MM')}`; + return `${translateLocal('travel.departs')} ${format(date, 'EEEE, MMM d, yyyy')} ${translateLocal('common.conjunctionAt')} ${format(date, 'HH:MM')}`; +} + +/** + * Returns a formatted flight date and hour. + * Dates are formatted as follows: + * 1. When the date refers to the current year: Wednesday, Mar 17 8:00 AM + * 2. When the date refers not to the current year: Wednesday, Mar 17, 2023 8:00 AM + */ +function getFormattedTransportDateAndHour(date: Date): {date: string; hour: string} { + if (isThisYear(date)) { + return { + date: format(date, 'EEEE, MMM d'), + hour: format(date, 'h:mm a'), + }; + } + return { + date: format(date, 'EEEE, MMM d, yyyy'), + hour: format(date, 'h:mm a'), + }; } /** @@ -903,6 +918,7 @@ const DateUtils = { getFormattedDateRange, getFormattedReservationRangeDate, getFormattedTransportDate, + getFormattedTransportDateAndHour, doesDateBelongToAPastYear, isCardExpired, getDifferenceInDaysFromNow, From aa0fb9403d8c8d67ec0dc4800e859c6fe01f9610 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 27 Nov 2024 18:33:36 +0100 Subject: [PATCH 045/240] Add reservation index to display just one flight --- src/libs/TripReservationUtils.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libs/TripReservationUtils.ts b/src/libs/TripReservationUtils.ts index a8714122da97..6ddf0dc30f57 100644 --- a/src/libs/TripReservationUtils.ts +++ b/src/libs/TripReservationUtils.ts @@ -63,16 +63,17 @@ function getTripReservationIcon(reservationType?: ReservationType): IconAsset { } } -type ReservationData = {reservation: Reservation; transactionID: string; reportID: string}; +type ReservationData = {reservation: Reservation; transactionID: string; reportID: string; reservationIndex: number}; function getReservationsFromTripTransactions(transactions: Transaction[]): ReservationData[] { return transactions .flatMap( (item) => - item?.receipt?.reservationList?.map((reservation) => ({ + item?.receipt?.reservationList?.map((reservation, reservationIndex) => ({ reservation, transactionID: item.transactionID, reportID: item.reportID, + reservationIndex, })) ?? [], ) .sort((a, b) => new Date(a.reservation.start.date).getTime() - new Date(b.reservation.start.date).getTime()); From 1d541a1e95271c6682c74a7ad9f55c23363952f4 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 27 Nov 2024 18:33:48 +0100 Subject: [PATCH 046/240] Update config configuration --- src/libs/Navigation/linkingConfig/config.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 317c1e15e04f..2cf2f28a08e7 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -1320,7 +1320,13 @@ const config: LinkingOptions['config'] = { screens: { [SCREENS.TRAVEL.MY_TRIPS]: ROUTES.TRAVEL_MY_TRIPS, [SCREENS.TRAVEL.TCS]: ROUTES.TRAVEL_TCS, - [SCREENS.TRAVEL.TRIP_DETAILS]: ROUTES.TRAVEL_TRIP_DETAILS.route, + [SCREENS.TRAVEL.TRIP_SUMMARY]: ROUTES.TRAVEL_TRIP_SUMMARY.route, + [SCREENS.TRAVEL.TRIP_DETAILS]: { + path: ROUTES.TRAVEL_TRIP_DETAILS.route, + parse: { + reservationIndex: (reservationIndex: string) => parseInt(reservationIndex, 10), + }, + }, }, }, [SCREENS.RIGHT_MODAL.SEARCH_REPORT]: { From fe182384b590466d33be942542788135551a16d1 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 27 Nov 2024 18:34:03 +0100 Subject: [PATCH 047/240] Update styles after design changes --- src/pages/Travel/CarTripDetails.tsx | 19 ++- src/pages/Travel/FlightTripDetails.tsx | 171 ++++++++++++------------- src/pages/Travel/HotelTripDetails.tsx | 24 ++-- src/pages/Travel/TripDetailsPage.tsx | 3 +- 4 files changed, 108 insertions(+), 109 deletions(-) diff --git a/src/pages/Travel/CarTripDetails.tsx b/src/pages/Travel/CarTripDetails.tsx index 25c417425a9f..ceff885c349b 100644 --- a/src/pages/Travel/CarTripDetails.tsx +++ b/src/pages/Travel/CarTripDetails.tsx @@ -4,7 +4,6 @@ import type {OnyxEntry} from 'react-native-onyx'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; -import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; @@ -32,8 +31,8 @@ function CarTripDetails({transaction, personalDetails}: CarTripDetailsProps) { } const reservationIcon = TripReservationUtils.getTripReservationIcon(carReservation.type); - const checkInDate = DateUtils.getFormattedTransportDate(new Date(carReservation.start.date), true); - const checkOutDate = DateUtils.getFormattedTransportDate(new Date(carReservation.end.date), true); + const pickUpDate = DateUtils.getFormattedTransportDateAndHour(new Date(carReservation.start.date)); + const dropOffDate = DateUtils.getFormattedTransportDateAndHour(new Date(carReservation.end.date)); return ( <> @@ -46,12 +45,10 @@ function CarTripDetails({transaction, personalDetails}: CarTripDetailsProps) { interactive={false} wrapperStyle={styles.pb3} /> - {translate('travel.carDetails.rentalCar')} - {carReservation.vendor} ; personalDetails: OnyxEntry; + reservationIndex: number; }; -function FlightTripDetails({transaction, personalDetails}: FlightTripDetailsProps) { +function FlightTripDetails({transaction, personalDetails, reservationIndex}: FlightTripDetailsProps) { const styles = useThemeStyles(); const theme = useTheme(); const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); + const flightReservation = transaction?.receipt?.reservationList?.at(reservationIndex); + + if (!flightReservation) { + return null; + } + + const reservationIcon = TripReservationUtils.getTripReservationIcon(flightReservation.type); + const startDate = DateUtils.getFormattedTransportDateAndHour(new Date(flightReservation.start.date)); + const endDate = DateUtils.getFormattedTransportDateAndHour(new Date(flightReservation.end.date)); + + const prevFlightStartDate = reservationIndex > 0 && transaction?.receipt?.reservationList?.at(reservationIndex - 1)?.end.date; + const layover = prevFlightStartDate && DateUtils.getFormattedDurationBetweenDates(new Date(prevFlightStartDate), new Date(flightReservation.start.date)); + return ( <> - {transaction?.receipt?.reservationList?.map((reservation, index) => { - const reservationIcon = TripReservationUtils.getTripReservationIcon(reservation.type); - const startDate = DateUtils.getFormattedTransportDate(new Date(reservation.start.date), true); - const endDate = DateUtils.getFormattedTransportDate(new Date(reservation.end.date), true); + {!!layover && ( + <> + + + + )} - const nextFlightStartDate = transaction.receipt?.reservationList?.at(index + 1)?.start.date; - const layover = nextFlightStartDate && DateUtils.getFormattedDurationBetweenDates(new Date(reservation.end.date), new Date(nextFlightStartDate)); + + + - return ( - <> - {translate('travel.flight')} - - {reservation.start.cityName} ({reservation.start.shortName}) {translate('common.conjunctionTo')} {reservation.end.cityName} ({reservation.end.shortName}) - - + {!!flightReservation.route?.number && ( + + + + )} + {!!flightReservation.route?.class && ( + + + )} + {!!flightReservation.confirmations?.at(0)?.value && ( + - - - {!!reservation.route?.number && ( - - - - )} - {!!reservation.route?.class && ( - - - - )} - {!!reservation.confirmations?.at(0)?.value && ( - - - - )} - - - {!!layover && ( - <> - - - - )} - - ); - })} + + )} + ); } diff --git a/src/pages/Travel/HotelTripDetails.tsx b/src/pages/Travel/HotelTripDetails.tsx index 32f0a6836828..4ce3fbabb7ad 100644 --- a/src/pages/Travel/HotelTripDetails.tsx +++ b/src/pages/Travel/HotelTripDetails.tsx @@ -4,7 +4,6 @@ import type {OnyxEntry} from 'react-native-onyx'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; -import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; @@ -32,8 +31,8 @@ function HotelTripDetails({transaction, personalDetails}: HotelTripDetailsProps) } const reservationIcon = TripReservationUtils.getTripReservationIcon(hotelReservation.type); - const checkInDate = DateUtils.getFormattedTransportDate(new Date(hotelReservation.start.date), true); - const checkOutDate = DateUtils.getFormattedTransportDate(new Date(hotelReservation.end.date), true); + const checkInDate = DateUtils.getFormattedTransportDateAndHour(new Date(hotelReservation.start.date)); + const checkOutDate = DateUtils.getFormattedTransportDateAndHour(new Date(hotelReservation.end.date)); return ( <> @@ -46,12 +45,10 @@ function HotelTripDetails({transaction, personalDetails}: HotelTripDetailsProps) interactive={false} wrapperStyle={styles.pb3} /> - {translate('travel.hotel')} - {hotelReservation.start.longName} + {!!hotelReservation.confirmations?.at(0)?.value && ( diff --git a/src/pages/Travel/TripDetailsPage.tsx b/src/pages/Travel/TripDetailsPage.tsx index 971128101486..96d7ec2b56ba 100644 --- a/src/pages/Travel/TripDetailsPage.tsx +++ b/src/pages/Travel/TripDetailsPage.tsx @@ -52,7 +52,7 @@ function TripDetailsPage({route}: TripDetailsPageProps) { shouldShow={!canUseSpotnanaTravel && !NativeModules.HybridAppModule} > @@ -60,6 +60,7 @@ function TripDetailsPage({route}: TripDetailsPageProps) { )} {reservationType === 'hotel' && ( From c882db81eb5712c02fb0b01a652163b651a87e2d Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 27 Nov 2024 18:52:46 +0100 Subject: [PATCH 048/240] Rename TRIP_DETAILS screen identifier to maintain consistent naming convention --- src/SCREENS.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 2efe8084b3bb..5b708e537147 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -28,7 +28,7 @@ const SCREENS = { MY_TRIPS: 'Travel_MyTrips', TCS: 'Travel_TCS', TRIP_SUMMARY: 'Travel_TripSummary', - TRIP_DETAILS: 'Trip_Details', + TRIP_DETAILS: 'Travel_TripDetails', }, SEARCH: { CENTRAL_PANE: 'Search_Central_Pane', From c20e09e6810ddc7f4979d5fc8e72d3116fdff964 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 27 Nov 2024 18:54:41 +0100 Subject: [PATCH 049/240] Fix typecheck and lint --- src/libs/ReportUtils.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 16d1d65d99f1..c647223a4715 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -860,7 +860,7 @@ function isChatReport(report: OnyxEntry): boolean { return report?.type === CONST.REPORT.TYPE.CHAT; } -function isInvoiceReport(report: OnyxInputOrEntry | SearchReport): report is Report { +function isInvoiceReport(report: OnyxInputOrEntry | SearchReport): boolean { return report?.type === CONST.REPORT.TYPE.INVOICE; } @@ -1667,8 +1667,6 @@ function isMoneyRequest(reportOrID: OnyxEntry | string): boolean { /** * Checks if a report is an IOU or expense report. */ -function isMoneyRequestReport(reportOrID: OnyxInputOrEntry): reportOrID is Report; -function isMoneyRequestReport(reportOrID: SearchReport | string): boolean; function isMoneyRequestReport(reportOrID: OnyxInputOrEntry | SearchReport | string): boolean { const report = typeof reportOrID === 'string' ? ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] ?? null : reportOrID; return isIOUReport(report) || isExpenseReport(report); @@ -2848,7 +2846,7 @@ function hasNonReimbursableTransactions(iouReportID: string | undefined): boolea function getMoneyRequestSpendBreakdown(report: OnyxInputOrEntry, allReportsDict?: OnyxCollection): SpendBreakdown { const allAvailableReports = allReportsDict ?? ReportConnection.getAllReports(); let moneyRequestReport: OnyxEntry; - if (isMoneyRequestReport(report) || isInvoiceReport(report)) { + if (report && (isMoneyRequestReport(report) || isInvoiceReport(report))) { moneyRequestReport = report; } if (allAvailableReports && report?.iouReportID) { From 7dcfbd0159a50dba5fd7a7a4f559f844a5d8bcb6 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 27 Nov 2024 19:01:16 +0100 Subject: [PATCH 050/240] Add Spanish translations for flight, hotel, and car details --- src/languages/es.ts | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/languages/es.ts b/src/languages/es.ts index 1897e10ccaac..3710c3080c99 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2449,9 +2449,36 @@ const translations = { error: 'Debes aceptar los Términos y condiciones para que el viaje continúe', }, flight: 'Vuelo', + flightDetails: { + layover: 'Escala', + takeOff: 'Despegue', + landing: 'Aterrizaje', + passenger: 'Pasajero', + seat: 'Asiento', + class: 'Clase de cabina', + recordLocator: 'Localizador de la reserva', + }, hotel: 'Hotel', + hotelDetails: { + guest: 'Cliente', + checkIn: 'Entrada', + checkOut: 'Salida', + roomType: 'Tipo de habitación', + cancellation: 'Política de cancelación', + confirmation: 'Número de confirmación', + }, car: 'Auto', + carDetails: { + rentalCar: 'Coche de alquiler', + pickUp: 'Recogida', + dropOff: 'Devolución', + driver: 'Conductor', + carType: 'Tipo de coche', + cancellation: 'Política de cancelación', + confirmation: 'Número de confirmación', + }, viewTrip: 'Ver viaje', + modifyTrip: 'Modificar viaje', tripSupport: 'Soporte de Viaje', viewTripDetails: 'Ver detalles del viaje', trip: 'Viaje', From 994833ecd8fc1d60e862d2090902ddf251db9bbb Mon Sep 17 00:00:00 2001 From: mkzie2 Date: Thu, 28 Nov 2024 12:13:31 +0700 Subject: [PATCH 051/240] fix: navigate to LHN after onboarding --- src/libs/navigateAfterOnboarding.ts | 17 ++--------------- .../BaseOnboardingAccounting.tsx | 6 +++--- .../BaseOnboardingPersonalDetails.tsx | 8 ++++---- 3 files changed, 9 insertions(+), 22 deletions(-) diff --git a/src/libs/navigateAfterOnboarding.ts b/src/libs/navigateAfterOnboarding.ts index d84927988b5c..a57363c05e4f 100644 --- a/src/libs/navigateAfterOnboarding.ts +++ b/src/libs/navigateAfterOnboarding.ts @@ -1,17 +1,9 @@ import ROUTES from '@src/ROUTES'; -import * as Report from './actions/Report'; import Navigation from './Navigation/Navigation'; import shouldOpenOnAdminRoom from './Navigation/shouldOpenOnAdminRoom'; import * as ReportUtils from './ReportUtils'; -const navigateAfterOnboarding = ( - isSmallScreenWidth: boolean, - shouldUseNarrowLayout: boolean, - canUseDefaultRooms: boolean | undefined, - onboardingPolicyID?: string, - activeWorkspaceID?: string, - backTo?: string, -) => { +const navigateAfterOnboarding = (isSmallScreenWidth: boolean, canUseDefaultRooms: boolean | undefined, onboardingPolicyID?: string, activeWorkspaceID?: string) => { Navigation.dismissModal(); // When hasCompletedGuidedSetupFlow is true, OnboardingModalNavigator in AuthScreen is removed from the navigation stack. @@ -24,12 +16,7 @@ const navigateAfterOnboarding = ( const lastAccessedReport = ReportUtils.findLastAccessedReport(!canUseDefaultRooms, shouldOpenOnAdminRoom(), activeWorkspaceID); const lastAccessedReportID = lastAccessedReport?.reportID; // we don't want to navigate to newly creaded workspace after onboarding completed. - if (!lastAccessedReportID || lastAccessedReport.policyID === onboardingPolicyID) { - // Only navigate to concierge chat when central pane is visible - // Otherwise stay on the chats screen. - if (!shouldUseNarrowLayout && !backTo) { - Report.navigateToConciergeChat(); - } + if (!lastAccessedReportID || lastAccessedReport.policyID === onboardingPolicyID || ReportUtils.isConciergeChatReport(lastAccessedReport)) { return; } diff --git a/src/pages/OnboardingAccounting/BaseOnboardingAccounting.tsx b/src/pages/OnboardingAccounting/BaseOnboardingAccounting.tsx index d9f0005ff4a0..b4e96cb86a1d 100644 --- a/src/pages/OnboardingAccounting/BaseOnboardingAccounting.tsx +++ b/src/pages/OnboardingAccounting/BaseOnboardingAccounting.tsx @@ -35,7 +35,7 @@ type OnboardingListItem = ListItem & { keyForList: OnboardingAccounting; }; -function BaseOnboardingAccounting({shouldUseNativeStyles, route}: BaseOnboardingAccountingProps) { +function BaseOnboardingAccounting({shouldUseNativeStyles}: BaseOnboardingAccountingProps) { const styles = useThemeStyles(); const theme = useTheme(); const StyleUtils = useStyleUtils(); @@ -43,7 +43,7 @@ function BaseOnboardingAccounting({shouldUseNativeStyles, route}: BaseOnboarding // We need to use isSmallScreenWidth, see navigateAfterOnboarding function comment // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth - const {onboardingIsMediumOrLargerScreenWidth, isSmallScreenWidth, shouldUseNarrowLayout} = useResponsiveLayout(); + const {onboardingIsMediumOrLargerScreenWidth, isSmallScreenWidth} = useResponsiveLayout(); const [onboardingValues] = useOnyx(ONYXKEYS.NVP_ONBOARDING); const [onboardingPurposeSelected] = useOnyx(ONYXKEYS.ONBOARDING_PURPOSE_SELECTED); const [onboardingPolicyID, onboardingPolicyIDResult] = useOnyx(ONYXKEYS.ONBOARDING_POLICY_ID); @@ -169,7 +169,7 @@ function BaseOnboardingAccounting({shouldUseNativeStyles, route}: BaseOnboarding Welcome.setOnboardingAdminsChatReportID(); Welcome.setOnboardingPolicyID(); }); - navigateAfterOnboarding(isSmallScreenWidth, shouldUseNarrowLayout, canUseDefaultRooms, onboardingPolicyID, activeWorkspaceID, route.params?.backTo); + navigateAfterOnboarding(isSmallScreenWidth, canUseDefaultRooms, onboardingPolicyID, activeWorkspaceID); }} pressOnEnter /> diff --git a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx index 54b485bd54dd..ad3e22e41e1d 100644 --- a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx +++ b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx @@ -29,7 +29,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import INPUT_IDS from '@src/types/form/DisplayNameForm'; import type {BaseOnboardingPersonalDetailsProps} from './types'; -function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNativeStyles, route}: BaseOnboardingPersonalDetailsProps) { +function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNativeStyles}: BaseOnboardingPersonalDetailsProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const [onboardingPurposeSelected] = useOnyx(ONYXKEYS.ONBOARDING_PURPOSE_SELECTED); @@ -38,7 +38,7 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat // We need to use isSmallScreenWidth, see navigateAfterOnboarding function comment // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth - const {onboardingIsMediumOrLargerScreenWidth, isSmallScreenWidth, shouldUseNarrowLayout} = useResponsiveLayout(); + const {onboardingIsMediumOrLargerScreenWidth, isSmallScreenWidth} = useResponsiveLayout(); const {inputCallbackRef} = useAutoFocusInput(); const [shouldValidateOnChange, setShouldValidateOnChange] = useState(false); const {isOffline} = useNetwork(); @@ -72,9 +72,9 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat Welcome.setOnboardingAdminsChatReportID(); Welcome.setOnboardingPolicyID(); - navigateAfterOnboarding(isSmallScreenWidth, shouldUseNarrowLayout, canUseDefaultRooms, onboardingPolicyID, activeWorkspaceID, route.params?.backTo); + navigateAfterOnboarding(isSmallScreenWidth, canUseDefaultRooms, onboardingPolicyID, activeWorkspaceID); }, - [onboardingPurposeSelected, onboardingAdminsChatReportID, onboardingPolicyID, route.params?.backTo, activeWorkspaceID, canUseDefaultRooms, isSmallScreenWidth, shouldUseNarrowLayout], + [onboardingPurposeSelected, onboardingAdminsChatReportID, onboardingPolicyID, activeWorkspaceID, canUseDefaultRooms, isSmallScreenWidth], ); const validate = (values: FormOnyxValues<'onboardingPersonalDetailsForm'>) => { From 5ba408aa59533d7f32c22e48d69bc1340b8cff2a Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 28 Nov 2024 14:52:46 +0100 Subject: [PATCH 052/240] Refactor reservation type checks to use constants --- src/pages/Travel/TripDetailsPage.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/Travel/TripDetailsPage.tsx b/src/pages/Travel/TripDetailsPage.tsx index 96d7ec2b56ba..ee7328299219 100644 --- a/src/pages/Travel/TripDetailsPage.tsx +++ b/src/pages/Travel/TripDetailsPage.tsx @@ -56,20 +56,20 @@ function TripDetailsPage({route}: TripDetailsPageProps) { shouldShowBackButton /> - {reservationType === 'flight' && ( + {reservationType === CONST.RESERVATION_TYPE.FLIGHT && ( )} - {reservationType === 'hotel' && ( + {reservationType === CONST.RESERVATION_TYPE.HOTEL && ( )} - {reservationType === 'car' && ( + {reservationType === CONST.RESERVATION_TYPE.CAR && ( Date: Thu, 28 Nov 2024 15:45:44 +0100 Subject: [PATCH 053/240] Add cancellation deadline and policy to car and hotel trip details --- src/languages/en.ts | 2 ++ src/pages/Travel/CarTripDetails.tsx | 16 ++++++++++++++-- src/pages/Travel/HotelTripDetails.tsx | 14 +++++++++++++- src/types/onyx/Transaction.ts | 6 ++++++ 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 148f3463495f..753b4600e78d 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2441,6 +2441,7 @@ const translations = { checkOut: 'Check-out', roomType: 'Room type', cancellation: 'Cancellation policy', + cancellationUntil: 'Free cancellation until', confirmation: 'Confirmation number', }, car: 'Car', @@ -2451,6 +2452,7 @@ const translations = { driver: 'Driver', carType: 'Car type', cancellation: 'Cancellation policy', + cancellationUntil: 'Free cancellation until', confirmation: 'Confirmation number', }, viewTrip: 'View trip', diff --git a/src/pages/Travel/CarTripDetails.tsx b/src/pages/Travel/CarTripDetails.tsx index ceff885c349b..5e87b0f5b79f 100644 --- a/src/pages/Travel/CarTripDetails.tsx +++ b/src/pages/Travel/CarTripDetails.tsx @@ -33,6 +33,9 @@ function CarTripDetails({transaction, personalDetails}: CarTripDetailsProps) { const reservationIcon = TripReservationUtils.getTripReservationIcon(carReservation.type); const pickUpDate = DateUtils.getFormattedTransportDateAndHour(new Date(carReservation.start.date)); const dropOffDate = DateUtils.getFormattedTransportDateAndHour(new Date(carReservation.end.date)); + const cancellationText = carReservation.deadline + ? `${translate('travel.carDetails.cancellationUntil')} ${DateUtils.getFormattedTransportDateAndHour(new Date(carReservation.deadline)).date}` + : carReservation.cancellationPolicy; return ( <> @@ -74,7 +77,7 @@ function CarTripDetails({transaction, personalDetails}: CarTripDetailsProps) { /> {!!carReservation.carInfo?.name && ( - + )} + {!!cancellationText && ( + + + + )} {!!carReservation.reservationID && ( - + @@ -73,8 +76,17 @@ function HotelTripDetails({transaction, personalDetails}: HotelTripDetailsProps) title={checkOutDate.hour} interactive={false} /> + {!!cancellationText && ( + + + + )} {!!hotelReservation.confirmations?.at(0)?.value && ( - + Date: Thu, 28 Nov 2024 16:30:39 +0100 Subject: [PATCH 054/240] rename folder --- src/CONST.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CONST.ts b/src/CONST.ts index 3ff41b466c79..f33041921a7b 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -5956,7 +5956,7 @@ const CONST = { DOWNLOADS_PATH: '/Downloads', DOWNLOADS_TIMEOUT: 5000, NEW_EXPENSIFY_PATH: '/New Expensify', - RECEIPTS_UPLOAD_PATH: '/Receipts-Pending-Upload', + RECEIPTS_UPLOAD_PATH: '/Receipts-Upload', ENVIRONMENT_SUFFIX: { DEV: ' Dev', From 2a605bac8e9c74bfbb1ac62d9fd427be304520da Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Thu, 28 Nov 2024 16:32:16 +0100 Subject: [PATCH 055/240] rename import --- src/libs/getReceiptsUploadFolderPath/index.android.ts | 4 ++-- src/libs/getReceiptsUploadFolderPath/index.ios.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libs/getReceiptsUploadFolderPath/index.android.ts b/src/libs/getReceiptsUploadFolderPath/index.android.ts index 5179afd64134..d88f83994524 100644 --- a/src/libs/getReceiptsUploadFolderPath/index.android.ts +++ b/src/libs/getReceiptsUploadFolderPath/index.android.ts @@ -1,7 +1,7 @@ -import ReactNativeBlobUtil from 'react-native-blob-util'; +import RNFetchBlob from 'react-native-blob-util'; import CONST from '@src/CONST'; import type GetReceiptsUploadFolderPath from './types'; -const getReceiptsUploadFolderPath: GetReceiptsUploadFolderPath = () => `${ReactNativeBlobUtil.fs.dirs.DownloadDir}${CONST.RECEIPTS_UPLOAD_PATH}`; +const getReceiptsUploadFolderPath: GetReceiptsUploadFolderPath = () => `${RNFetchBlob.fs.dirs.DownloadDir}${CONST.RECEIPTS_UPLOAD_PATH}`; export default getReceiptsUploadFolderPath; diff --git a/src/libs/getReceiptsUploadFolderPath/index.ios.ts b/src/libs/getReceiptsUploadFolderPath/index.ios.ts index c787909055a6..987d66bf17c0 100644 --- a/src/libs/getReceiptsUploadFolderPath/index.ios.ts +++ b/src/libs/getReceiptsUploadFolderPath/index.ios.ts @@ -1,7 +1,7 @@ -import ReactNativeBlobUtil from 'react-native-blob-util'; +import RNFetchBlob from 'react-native-blob-util'; import CONST from '@src/CONST'; import type GetReceiptsUploadFolderPath from './types'; -const getReceiptsUploadFolderPath: GetReceiptsUploadFolderPath = () => `${ReactNativeBlobUtil.fs.dirs.DocumentDir}${CONST.RECEIPTS_UPLOAD_PATH}`; +const getReceiptsUploadFolderPath: GetReceiptsUploadFolderPath = () => `${RNFetchBlob.fs.dirs.DocumentDir}${CONST.RECEIPTS_UPLOAD_PATH}`; export default getReceiptsUploadFolderPath; From b1d45dc33229adce6fa3b1f1a62a1d1a827c9927 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Thu, 28 Nov 2024 17:17:35 +0100 Subject: [PATCH 056/240] clear logs --- .../step/IOURequestStepScan/index.native.tsx | 27 +++++-------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx index 9704829c70c6..4af9b008f827 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx @@ -499,32 +499,21 @@ function IOURequestStepScan({ setDidCapturePhoto(true); - console.debug('[dev] Taking photo'); - const path = getReceiptsUploadFolderPath(); - console.debug('[dev] path', path); - ReactNativeBlobUtil.fs .isDir(path) .then((isDir) => { - console.debug('[dev] Checking if directory exists', isDir); - if (isDir) { return; } - ReactNativeBlobUtil.fs - .mkdir(path) - .then(() => { - console.debug('[dev] Directory created successfully'); - }) - .catch((error) => { - console.error('[dev] Error creating directory:', error); - }); + ReactNativeBlobUtil.fs.mkdir(path).catch((error: string) => { + Log.warn('Error creating the directory', error); + }); }) - .catch((error) => { - console.error('[dev] Error checking if directory exists:', error); + .catch((error: string) => { + Log.warn('Error checking if the directory exists', error); }) .then(() => { camera?.current @@ -534,8 +523,6 @@ function IOURequestStepScan({ path, }) .then((photo: PhotoFile) => { - console.debug('[dev] photo', photo); - // Store the receipt on the transaction object in Onyx const source = getPhotoSource(photo.path); IOU.setMoneyRequestReceipt(transactionID, source, photo.path, !isEditing); @@ -565,14 +552,14 @@ function IOURequestStepScan({ () => { setDidCapturePhoto(false); showCameraAlert(); - Log.warn('[dev] Error reading photo'); + Log.warn('Error reading photo'); }, ); }) .catch((error: string) => { setDidCapturePhoto(false); showCameraAlert(); - Log.warn('[dev] Error taking photo', error); + Log.warn('Error taking photo', error); }); }); }, [ From 1bb78b909b2c95cc47ea4f128e2ca722f6817063 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Fri, 29 Nov 2024 11:49:48 +0100 Subject: [PATCH 057/240] Fix mutation dropping --- ...x-dropping-mutations-in-transactions.patch | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 patches/react-native+0.75.2+024+fix-dropping-mutations-in-transactions.patch diff --git a/patches/react-native+0.75.2+024+fix-dropping-mutations-in-transactions.patch b/patches/react-native+0.75.2+024+fix-dropping-mutations-in-transactions.patch new file mode 100644 index 000000000000..974a0d090fb9 --- /dev/null +++ b/patches/react-native+0.75.2+024+fix-dropping-mutations-in-transactions.patch @@ -0,0 +1,67 @@ +diff --git a/node_modules/react-native/ReactAndroid/src/main/jni/react/fabric/Binding.cpp b/node_modules/react-native/ReactAndroid/src/main/jni/react/fabric/Binding.cpp +index 572fb3d..0efa1ed 100644 +--- a/node_modules/react-native/ReactAndroid/src/main/jni/react/fabric/Binding.cpp ++++ b/node_modules/react-native/ReactAndroid/src/main/jni/react/fabric/Binding.cpp +@@ -468,7 +468,7 @@ void Binding::schedulerDidFinishTransaction( + mountingTransaction->getSurfaceId(); + }); + +- if (pendingTransaction != pendingTransactions_.end()) { ++ if (pendingTransaction != pendingTransactions_.end() && pendingTransaction->canMergeWith(*mountingTransaction)) { + pendingTransaction->mergeWith(std::move(*mountingTransaction)); + } else { + pendingTransactions_.push_back(std::move(*mountingTransaction)); +diff --git a/node_modules/react-native/ReactCommon/react/renderer/mounting/MountingTransaction.cpp b/node_modules/react-native/ReactCommon/react/renderer/mounting/MountingTransaction.cpp +index d7dd1bc..d95d779 100644 +--- a/node_modules/react-native/ReactCommon/react/renderer/mounting/MountingTransaction.cpp ++++ b/node_modules/react-native/ReactCommon/react/renderer/mounting/MountingTransaction.cpp +@@ -5,6 +5,8 @@ + * LICENSE file in the root directory of this source tree. + */ + ++#include ++ + #include "MountingTransaction.h" + + namespace facebook::react { +@@ -54,4 +56,21 @@ void MountingTransaction::mergeWith(MountingTransaction&& transaction) { + telemetry_ = std::move(transaction.telemetry_); + } + ++bool MountingTransaction::canMergeWith(MountingTransaction& transaction) { ++ std::set deletedTags; ++ for (const auto& mutation : mutations_) { ++ if (mutation.type == ShadowViewMutation::Type::Delete) { ++ deletedTags.insert(mutation.oldChildShadowView.tag); ++ } ++ } ++ ++ for (const auto& mutation : transaction.getMutations()) { ++ if (mutation.type == ShadowViewMutation::Type::Create && deletedTags.contains(mutation.newChildShadowView.tag)) { ++ return false; ++ } ++ } ++ ++ return true; ++} ++ + } // namespace facebook::react +diff --git a/node_modules/react-native/ReactCommon/react/renderer/mounting/MountingTransaction.h b/node_modules/react-native/ReactCommon/react/renderer/mounting/MountingTransaction.h +index 277e9f4..38629db 100644 +--- a/node_modules/react-native/ReactCommon/react/renderer/mounting/MountingTransaction.h ++++ b/node_modules/react-native/ReactCommon/react/renderer/mounting/MountingTransaction.h +@@ -85,6 +85,14 @@ class MountingTransaction final { + */ + void mergeWith(MountingTransaction&& transaction); + ++ /* ++ * Checks whether the two transactions can be safely merged. Due to ++ * reordering of mutations during mount, the sequence of ++ * REMOVE -> DELETE | CREATE -> INSERT (2 transactions) may get changed to ++ * INSERT -> REMOVE -> DELETE and the state will diverge from there. ++ */ ++ bool canMergeWith(MountingTransaction& transaction); ++ + private: + SurfaceId surfaceId_; + Number number_; From 0389b737e12da98f81af5b2fe4cc951c9bbb936d Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 29 Nov 2024 13:51:28 +0100 Subject: [PATCH 058/240] Rename reservation deadline property and update related components to use the new name --- src/libs/DateUtils.ts | 8 ++++++-- src/pages/Travel/CarTripDetails.tsx | 4 ++-- src/pages/Travel/HotelTripDetails.tsx | 5 +++-- src/types/onyx/Transaction.ts | 2 +- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/libs/DateUtils.ts b/src/libs/DateUtils.ts index 21c0a2b7e375..8bdbdd2d21d0 100644 --- a/src/libs/DateUtils.ts +++ b/src/libs/DateUtils.ts @@ -832,10 +832,14 @@ function getFormattedTransportDateAndHour(date: Date): {date: string; hour: stri /** * Returns a formatted layover duration in format "2h 30m". */ -function getFormattedDurationBetweenDates(start: Date, end: Date): string { +function getFormattedDurationBetweenDates(start: Date, end: Date): string | undefined { const {days, hours, minutes} = intervalToDuration({start, end}); - return `${days ? `${days}d ` : ''}${hours ? `${hours}h ` : ''}${minutes}m`; + if (days && days > 0) { + return; + } + + return `${hours ? `${hours}h ` : ''}${minutes}m`; } function doesDateBelongToAPastYear(date: string): boolean { diff --git a/src/pages/Travel/CarTripDetails.tsx b/src/pages/Travel/CarTripDetails.tsx index 5e87b0f5b79f..4ad406e9b868 100644 --- a/src/pages/Travel/CarTripDetails.tsx +++ b/src/pages/Travel/CarTripDetails.tsx @@ -33,8 +33,8 @@ function CarTripDetails({transaction, personalDetails}: CarTripDetailsProps) { const reservationIcon = TripReservationUtils.getTripReservationIcon(carReservation.type); const pickUpDate = DateUtils.getFormattedTransportDateAndHour(new Date(carReservation.start.date)); const dropOffDate = DateUtils.getFormattedTransportDateAndHour(new Date(carReservation.end.date)); - const cancellationText = carReservation.deadline - ? `${translate('travel.carDetails.cancellationUntil')} ${DateUtils.getFormattedTransportDateAndHour(new Date(carReservation.deadline)).date}` + const cancellationText = carReservation.cancellationDeadline + ? `${translate('travel.carDetails.cancellationUntil')} ${DateUtils.getFormattedTransportDateAndHour(new Date(carReservation.cancellationDeadline)).date}` : carReservation.cancellationPolicy; return ( diff --git a/src/pages/Travel/HotelTripDetails.tsx b/src/pages/Travel/HotelTripDetails.tsx index 6e3e5e8c8bfb..f0fa279fbbe2 100644 --- a/src/pages/Travel/HotelTripDetails.tsx +++ b/src/pages/Travel/HotelTripDetails.tsx @@ -30,11 +30,12 @@ function HotelTripDetails({transaction, personalDetails}: HotelTripDetailsProps) return null; } + const reservationIcon = TripReservationUtils.getTripReservationIcon(hotelReservation.type); const checkInDate = DateUtils.getFormattedTransportDateAndHour(new Date(hotelReservation.start.date)); const checkOutDate = DateUtils.getFormattedTransportDateAndHour(new Date(hotelReservation.end.date)); - const cancellationText = hotelReservation.deadline - ? `${translate('travel.hotelDetails.cancellationUntil')} ${DateUtils.getFormattedTransportDateAndHour(new Date(hotelReservation.deadline)).date}` + const cancellationText = hotelReservation.cancellationDeadline + ? `${translate('travel.hotelDetails.cancellationUntil')} ${DateUtils.getFormattedTransportDateAndHour(new Date(hotelReservation.cancellationDeadline)).date}` : hotelReservation.cancellationPolicy; return ( diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 703769dfabf7..991b5230440f 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -211,7 +211,7 @@ type Reservation = { cancellationPolicy?: string; /** In car and hotel reservations, this represents the cancellation deadline */ - deadline?: string; + cancellationDeadline?: string; /** Collection of passenger confirmations */ confirmations?: ReservationConfirmation[]; From f906103b84cadcd8612120cbe0fefb592af54afd Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 29 Nov 2024 14:30:47 +0100 Subject: [PATCH 059/240] Rename displayName for CarTripDetails component --- src/pages/Travel/CarTripDetails.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Travel/CarTripDetails.tsx b/src/pages/Travel/CarTripDetails.tsx index 4ad406e9b868..711e2b07332c 100644 --- a/src/pages/Travel/CarTripDetails.tsx +++ b/src/pages/Travel/CarTripDetails.tsx @@ -107,6 +107,6 @@ function CarTripDetails({transaction, personalDetails}: CarTripDetailsProps) { ); } -CarTripDetails.displayName = 'FlightTripDetails'; +CarTripDetails.displayName = 'CarTripDetails'; export default CarTripDetails; From 79b0cbd84970de02ffeddc99d328729bd55fc81a Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 29 Nov 2024 14:35:31 +0100 Subject: [PATCH 060/240] Add Spanish translation for cancellationUntil in booking and car sections --- src/languages/es.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/languages/es.ts b/src/languages/es.ts index 9a8a6c69ebe3..c25a9a0f6027 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2474,6 +2474,7 @@ const translations = { checkOut: 'Salida', roomType: 'Tipo de habitación', cancellation: 'Política de cancelación', + cancellationUntil: 'Cancelación gratuita hasta el', confirmation: 'Número de confirmación', }, car: 'Auto', @@ -2484,6 +2485,7 @@ const translations = { driver: 'Conductor', carType: 'Tipo de coche', cancellation: 'Política de cancelación', + cancellationUntil: 'Cancelación gratuita hasta el', confirmation: 'Número de confirmación', }, viewTrip: 'Ver viaje', From 7bdea21dc5f71150e9e4c16b916b571dd796b896 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 29 Nov 2024 14:35:39 +0100 Subject: [PATCH 061/240] Remove unnecessary blank line in HotelTripDetails component --- src/pages/Travel/HotelTripDetails.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/Travel/HotelTripDetails.tsx b/src/pages/Travel/HotelTripDetails.tsx index f0fa279fbbe2..f0b16b0cb142 100644 --- a/src/pages/Travel/HotelTripDetails.tsx +++ b/src/pages/Travel/HotelTripDetails.tsx @@ -30,7 +30,6 @@ function HotelTripDetails({transaction, personalDetails}: HotelTripDetailsProps) return null; } - const reservationIcon = TripReservationUtils.getTripReservationIcon(hotelReservation.type); const checkInDate = DateUtils.getFormattedTransportDateAndHour(new Date(hotelReservation.start.date)); const checkOutDate = DateUtils.getFormattedTransportDateAndHour(new Date(hotelReservation.end.date)); From bde424e883f610c125de70f7cafc0760c2e8323a Mon Sep 17 00:00:00 2001 From: jayeshmangwani Date: Sun, 1 Dec 2024 02:20:31 +0530 Subject: [PATCH 062/240] added ws plan type page and added upgrade generic features view --- src/ROUTES.ts | 10 +- src/SCREENS.ts | 1 + src/languages/en.ts | 32 +++++ src/languages/es.ts | 32 +++++ src/languages/params.ts | 6 + .../parameters/UpgradeToCorporateParams.ts | 2 +- .../ModalStackNavigators/index.tsx | 1 + .../FULL_SCREEN_TO_RHP_MAPPING.ts | 1 + src/libs/Navigation/linkingConfig/config.ts | 3 + src/libs/Navigation/types.ts | 2 +- src/libs/PolicyUtils.ts | 12 ++ src/libs/actions/Policy/Policy.ts | 6 +- src/pages/workspace/WorkspaceProfilePage.tsx | 17 +++ .../WorkspaceProfilePlanTypePage.tsx | 128 ++++++++++++++++++ src/pages/workspace/WorkspacesListRow.tsx | 28 ++-- .../workspace/upgrade/GenericFeaturesView.tsx | 75 ++++++++++ src/pages/workspace/upgrade/UpgradeIntro.tsx | 13 +- .../upgrade/WorkspaceUpgradePage.tsx | 5 +- 18 files changed, 346 insertions(+), 28 deletions(-) create mode 100644 src/pages/workspace/WorkspaceProfilePlanTypePage.tsx create mode 100644 src/pages/workspace/upgrade/GenericFeaturesView.tsx diff --git a/src/ROUTES.ts b/src/ROUTES.ts index a6eb3c1166df..45a6345622fc 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -711,6 +711,10 @@ const ROUTES = { route: 'settings/workspaces/:policyID/profile/address', getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/profile/address` as const, backTo), }, + WORKSPACE_PROFILE_PLAN: { + route: 'settings/workspaces/:policyID/profile/plan', + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/profile/plan` as const, backTo), + }, WORKSPACE_ACCOUNTING: { route: 'settings/workspaces/:policyID/accounting', getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting` as const, @@ -974,9 +978,9 @@ const ROUTES = { getRoute: (policyID: string, categoryName: string) => `settings/workspaces/${policyID}/category/${encodeURIComponent(categoryName)}` as const, }, WORKSPACE_UPGRADE: { - route: 'settings/workspaces/:policyID/upgrade/:featureName', - getRoute: (policyID: string, featureName: string, backTo?: string) => - getUrlWithBackToParam(`settings/workspaces/${policyID}/upgrade/${encodeURIComponent(featureName)}` as const, backTo), + route: 'settings/workspaces/:policyID/upgrade/:featureName?', + getRoute: (policyID: string, featureName?: string, backTo?: string) => + getUrlWithBackToParam(`settings/workspaces/${policyID}/upgrade/${encodeURIComponent(featureName ?? '')}` as const, backTo), }, WORKSPACE_DOWNGRADE: { route: 'settings/workspaces/:policyID/downgrade/', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index e4fa03bf4815..1b4ecb1ea1c8 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -498,6 +498,7 @@ const SCREENS = { TAG_GL_CODE: 'Tag_GL_Code', CURRENCY: 'Workspace_Profile_Currency', ADDRESS: 'Workspace_Profile_Address', + PLAN: 'Workspace_Profile_Plan_Type', WORKFLOWS: 'Workspace_Workflows', WORKFLOWS_PAYER: 'Workspace_Workflows_Payer', WORKFLOWS_APPROVALS_NEW: 'Workspace_Approvals_New', diff --git a/src/languages/en.ts b/src/languages/en.ts index 13ff38a0bc58..54cff7b78575 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -191,6 +191,7 @@ import type { WelcomeNoteParams, WelcomeToRoomParams, WeSentYouMagicSignInLinkParams, + WorkspaceLockedPlanTypeParams, WorkspaceOwnerWillNeedToAddOrUpdatePaymentCardParams, YourPlanPriceParams, ZipCodeExampleFormatParams, @@ -2538,6 +2539,7 @@ const translations = { return 'Member'; } }, + planType: 'Plan type', submitExpense: 'Submit expenses using your workspace chat below:', defaultCategory: 'Default category', }, @@ -4280,6 +4282,19 @@ const translations = { moreDetails: 'for more details.', gotIt: 'Got it, thanks', }, + commonFeatures: { + title: 'Upgrade Workspace to Control', + note: 'Get access to all our most advanced functionality, including:', + benefits: { + note: 'The Control plan starts at $9 per active member per month.', + learnMore: 'Learn more', + pricing: 'about our plans and pricing.', + benefit1: 'Advanced accounting connections (NetSuite, Sage Intacct and more)', + benefit2: 'Expense rules', + benefit3: 'Multiple approval workflows', + benefit4: 'Enhanced security controls', + }, + }, }, restrictedAction: { restricted: 'Restricted', @@ -4383,6 +4398,23 @@ const translations = { andEnableWorkflows: 'and enable workflows, then add approvals to unlock this feature.', }, }, + planTypePage: { + planTypes: { + team: { + label: 'Collect', + description: 'For teams looking to automate their processes.', + }, + corporate: { + label: 'Control', + description: 'For organizations with advanced requirements.', + }, + }, + description: "Choose a plan that's right for you. For a detailed list of features and pricing, check out our", + subscriptionLink: 'plan types and pricing help page', + lockedPlanDescription: ({subscriptionUsersCount, annualSubscriptionEndDate}: WorkspaceLockedPlanTypeParams) => + `You've committed to ${subscriptionUsersCount} active users on the Control plan until your annual subscription ends on ${annualSubscriptionEndDate}. You can switch to pay-per-use subscription and downgrade to the Collect plan starting ${annualSubscriptionEndDate} by disabling auto-renew in`, + subscriptions: 'Subscriptions', + }, }, getAssistancePage: { title: 'Get assistance', diff --git a/src/languages/es.ts b/src/languages/es.ts index 6477827fd0a1..b40a65c6eb6e 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -191,6 +191,7 @@ import type { WelcomeNoteParams, WelcomeToRoomParams, WeSentYouMagicSignInLinkParams, + WorkspaceLockedPlanTypeParams, WorkspaceOwnerWillNeedToAddOrUpdatePaymentCardParams, YourPlanPriceParams, ZipCodeExampleFormatParams, @@ -2562,6 +2563,7 @@ const translations = { return 'Miembro'; } }, + planType: 'Tipo de plan', submitExpense: 'Envíe los gastos utilizando el chat de su espacio de trabajo:', defaultCategory: 'Categoría predeterminada', }, @@ -4247,6 +4249,23 @@ const translations = { confirmText: 'Sí, exportar de nuevo', cancelText: 'Cancelar', }, + planTypePage: { + planTypes: { + team: { + label: 'Collect', + description: 'Para equipos que buscan automatizar sus procesos.', + }, + corporate: { + label: 'Recolectar', + description: 'Para organizaciones con requisitos avanzados.', + }, + }, + description: 'Elige el plan adecuado para ti. Para ver una lista detallada de funciones y precios, consulta nuestra', + subscriptionLink: 'página de ayuda sobre tipos de planes y precios', + lockedPlanDescription: ({subscriptionUsersCount, annualSubscriptionEndDate}: WorkspaceLockedPlanTypeParams) => + `Tienes un compromiso anual de ${subscriptionUsersCount} miembros activos en el plan Control hasta el ${annualSubscriptionEndDate}. Puedes cambiar a una suscripción de pago por uso y desmejorar al plan Recopilar a partir del ${annualSubscriptionEndDate} desactivando la renovación automática en`, + subscriptions: 'Suscripciones', + }, upgrade: { reportFields: { title: 'Los campos', @@ -4328,6 +4347,19 @@ const translations = { moreDetails: 'para obtener más información.', gotIt: 'Entendido, gracias.', }, + commonFeatures: { + title: 'Actualiza tu espacio de trabajo al plan Controlar', + note: 'Obtén acceso a todas nuestras funciones más avanzadas, incluyendo:', + benefits: { + note: 'El plan Controlar comienza en $9 por miembro activo al mes.', + learnMore: 'Obtén más información', + pricing: 'sobre nuestros planes y precios.', + benefit1: 'Conexiones contables avanzadas (NetSuite, Sage Intacct y más)', + benefit2: 'Reglas de gastos', + benefit3: 'Flujos de aprobación múltiples', + benefit4: 'Controles de seguridad mejorados', + }, + }, }, restrictedAction: { restricted: 'Restringido', diff --git a/src/languages/params.ts b/src/languages/params.ts index 90b789efae58..9b9b3aeca092 100644 --- a/src/languages/params.ts +++ b/src/languages/params.ts @@ -559,6 +559,11 @@ type CurrencyCodeParams = { currencyCode: string; }; +type WorkspaceLockedPlanTypeParams = { + subscriptionUsersCount: number; + annualSubscriptionEndDate: string; +}; + type CompanyNameParams = { companyName: string; }; @@ -769,6 +774,7 @@ export type { ImportedTypesParams, ImportPerDiemRatesSuccessfullDescriptionParams, CurrencyCodeParams, + WorkspaceLockedPlanTypeParams, CompanyNameParams, ChatWithAccountManagerParams, }; diff --git a/src/libs/API/parameters/UpgradeToCorporateParams.ts b/src/libs/API/parameters/UpgradeToCorporateParams.ts index ee9d1359c4dd..7b7ff3e0adcc 100644 --- a/src/libs/API/parameters/UpgradeToCorporateParams.ts +++ b/src/libs/API/parameters/UpgradeToCorporateParams.ts @@ -1,6 +1,6 @@ type UpgradeToCorporateParams = { policyID: string; - featureName: string; + featureName?: string; }; export default UpgradeToCorporateParams; diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 9822c60faaa8..73b256ae6fbd 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -269,6 +269,7 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/WorkspaceProfileCurrencyPage').default, [SCREENS.WORKSPACE.CATEGORY_SETTINGS]: () => require('../../../../pages/workspace/categories/CategorySettingsPage').default, [SCREENS.WORKSPACE.ADDRESS]: () => require('../../../../pages/workspace/WorkspaceProfileAddressPage').default, + [SCREENS.WORKSPACE.PLAN]: () => require('../../../../pages/workspace/WorkspaceProfilePlanTypePage').default, [SCREENS.WORKSPACE.CATEGORIES_SETTINGS]: () => require('../../../../pages/workspace/categories/WorkspaceCategoriesSettingsPage').default, [SCREENS.WORKSPACE.CATEGORIES_IMPORT]: () => require('../../../../pages/workspace/categories/ImportCategoriesPage').default, [SCREENS.WORKSPACE.CATEGORIES_IMPORTED]: () => require('../../../../pages/workspace/categories/ImportedCategoriesPage').default, diff --git a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts index f36f154819f5..4365c5e65e25 100755 --- a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts @@ -5,6 +5,7 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial> = { [SCREENS.WORKSPACE.PROFILE]: [ SCREENS.WORKSPACE.NAME, SCREENS.WORKSPACE.ADDRESS, + SCREENS.WORKSPACE.PLAN, SCREENS.WORKSPACE.CURRENCY, SCREENS.WORKSPACE.DESCRIPTION, SCREENS.WORKSPACE.SHARE, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 02f7f6950a0d..e9f627ee4341 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -347,6 +347,9 @@ const config: LinkingOptions['config'] = { [SCREENS.WORKSPACE.ADDRESS]: { path: ROUTES.WORKSPACE_PROFILE_ADDRESS.route, }, + [SCREENS.WORKSPACE.PLAN]: { + path: ROUTES.WORKSPACE_PROFILE_PLAN.route, + }, [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_IMPORT]: {path: ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_IMPORT.route}, [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_CHART_OF_ACCOUNTS]: {path: ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_CHART_OF_ACCOUNTS.route}, [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_CLASSES]: {path: ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_CLASSES.route}, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 3674a24a907b..2bb786884bbe 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -246,7 +246,7 @@ type SettingsNavigatorParamList = { }; [SCREENS.WORKSPACE.UPGRADE]: { policyID: string; - featureName: string; + featureName?: string; backTo?: Routes; categoryId?: string; }; diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index f6b277d69d6b..562b2ba62198 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -1119,6 +1119,17 @@ function getActivePolicy(): OnyxEntry { return getPolicy(activePolicyId); } +function getUserFriendlyWorkspaceType(workspaceType: ValueOf) { + switch (workspaceType) { + case CONST.POLICY.TYPE.CORPORATE: + return Localize.translateLocal('workspace.type.control'); + case CONST.POLICY.TYPE.TEAM: + return Localize.translateLocal('workspace.type.collect'); + default: + return Localize.translateLocal('workspace.type.free'); + } +} + function isPolicyAccessible(policy: OnyxEntry): boolean { return !isEmptyObject(policy) && (Object.keys(policy).length !== 1 || isEmptyObject(policy.errors)) && !!policy?.id; } @@ -1252,6 +1263,7 @@ export { getNetSuiteImportCustomFieldLabel, getAllPoliciesLength, getActivePolicy, + getUserFriendlyWorkspaceType, isPolicyAccessible, areAllGroupPoliciesExpenseChatDisabled, }; diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 0b72d2de3f98..01a5909de9db 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -1663,6 +1663,7 @@ function buildPolicyData(policyOwnerEmail = '', makeMeAdmin = false, policyName outputCurrency: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, address: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, description: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + type: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, }, }, }, @@ -1735,6 +1736,7 @@ function buildPolicyData(policyOwnerEmail = '', makeMeAdmin = false, policyName outputCurrency: null, address: null, description: null, + type: null, }, }, }, @@ -3324,7 +3326,7 @@ function setForeignCurrencyDefault(policyID: string, taxCode: string) { API.write(WRITE_COMMANDS.SET_POLICY_TAXES_FOREIGN_CURRENCY_DEFAULT, parameters, onyxData); } -function upgradeToCorporate(policyID: string, featureName: string) { +function upgradeToCorporate(policyID: string, featureName?: string) { const policy = getPolicy(policyID); const optimisticData: OnyxUpdate[] = [ { @@ -3376,7 +3378,7 @@ function upgradeToCorporate(policyID: string, featureName: string) { }, ]; - const parameters: UpgradeToCorporateParams = {policyID, featureName}; + const parameters: UpgradeToCorporateParams = {policyID, ...(featureName ? {featureName} : {})}; API.write(WRITE_COMMANDS.UPGRADE_TO_CORPORATE, parameters, {optimisticData, successData, failureData}); } diff --git a/src/pages/workspace/WorkspaceProfilePage.tsx b/src/pages/workspace/WorkspaceProfilePage.tsx index 4a4862152d53..f46ac071a81b 100644 --- a/src/pages/workspace/WorkspaceProfilePage.tsx +++ b/src/pages/workspace/WorkspaceProfilePage.tsx @@ -77,6 +77,7 @@ function WorkspaceProfilePage({policyDraft, policy: policyProp, route}: Workspac const onPressName = useCallback(() => Navigation.navigate(ROUTES.WORKSPACE_PROFILE_NAME.getRoute(policy?.id ?? '-1')), [policy?.id]); const onPressDescription = useCallback(() => Navigation.navigate(ROUTES.WORKSPACE_PROFILE_DESCRIPTION.getRoute(policy?.id ?? '-1')), [policy?.id]); const onPressShare = useCallback(() => Navigation.navigate(ROUTES.WORKSPACE_PROFILE_SHARE.getRoute(policy?.id ?? '-1')), [policy?.id]); + const onPressPlanType = useCallback(() => Navigation.navigate(ROUTES.WORKSPACE_PROFILE_PLAN.getRoute(policy?.id ?? '-1')), [policy?.id]); const policyName = policy?.name ?? ''; const policyDescription = @@ -267,6 +268,22 @@ function WorkspaceProfilePage({policyDraft, policy: policyProp, route}: Workspac )} + {!readOnly && !!policy?.type && ( + + + + + + )} {!readOnly && (