From 890eba70df851f1613896c3df84e137538dead09 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 9 Sep 2024 10:46:29 +0700 Subject: [PATCH 001/438] Feature: add loading indicator when ReconnectApp is running --- src/pages/home/ReportScreen.tsx | 3 +++ .../sidebar/SidebarScreen/BaseSidebarScreen.tsx | 4 ++++ src/styles/index.ts | 13 +++++++++++++ 3 files changed, 20 insertions(+) diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 4eaf3633af38..be4cf15787a5 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -13,6 +13,7 @@ import DragAndDropProvider from '@components/DragAndDrop/Provider'; import MoneyReportHeader from '@components/MoneyReportHeader'; import MoneyRequestHeader from '@components/MoneyRequestHeader'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; +import ProgressBar from '@components/ProgressBar'; import ReportActionsSkeletonView from '@components/ReportActionsSkeletonView'; import ScreenWrapper from '@components/ScreenWrapper'; import TaskHeaderActionButton from '@components/TaskHeaderActionButton'; @@ -128,6 +129,7 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP); const [workspaceTooltip] = useOnyx(ONYXKEYS.NVP_WORKSPACE_TOOLTIP); const wasLoadingApp = usePrevious(isLoadingApp); + const [isLoadingReportData] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA, {initialValue: true}); const finishedLoadingApp = wasLoadingApp && !isLoadingApp; const isDeletedParentAction = ReportActionsUtils.isDeletedParentAction(parentReportAction); const prevIsDeletedParentAction = usePrevious(isDeletedParentAction); @@ -756,6 +758,7 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro needsOffscreenAlphaCompositing > {headerView} + {shouldUseNarrowLayout && } {ReportUtils.isTaskReport(report) && shouldUseNarrowLayout && ReportUtils.isOpenTaskReport(report, parentReportAction) && ( diff --git a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx index edc8dfb3cb3a..4626429fb53c 100644 --- a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx +++ b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx @@ -1,6 +1,7 @@ import React, {useEffect} from 'react'; import {View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; +import ProgressBar from '@components/ProgressBar'; import ScreenWrapper from '@components/ScreenWrapper'; import useActiveWorkspaceFromNavigationState from '@hooks/useActiveWorkspaceFromNavigationState'; import useLocalize from '@hooks/useLocalize'; @@ -29,6 +30,8 @@ function BaseSidebarScreen() { const {translate} = useLocalize(); const [activeWorkspace] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${activeWorkspaceID ?? -1}`); + const [isLoadingReportData] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA, {initialValue: true}); + useEffect(() => { Performance.markStart(CONST.TIMING.SIDEBAR_LOADED); Timing.start(CONST.TIMING.SIDEBAR_LOADED); @@ -57,6 +60,7 @@ function BaseSidebarScreen() { breadcrumbLabel={translate('common.inbox')} activeWorkspaceID={activeWorkspaceID} /> + top: 80, left: 12, }, + progressBarWrapper: { + height: 2, + width: '100%', + backgroundColor: '#1A3D32', + borderRadius: 5, + overflow: 'hidden', + }, + progressBar: { + height: '100%', + backgroundColor: '#03D47C', + borderRadius: 5, + width: '100%', + }, } satisfies Styles); type ThemeStyles = ReturnType; From 7a77276bd2414483144892228d98584faac03b9d Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 9 Sep 2024 10:55:23 +0700 Subject: [PATCH 002/438] fix ts check --- src/components/ProgressBar.tsx | 78 ++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 src/components/ProgressBar.tsx diff --git a/src/components/ProgressBar.tsx b/src/components/ProgressBar.tsx new file mode 100644 index 000000000000..d37d34887c03 --- /dev/null +++ b/src/components/ProgressBar.tsx @@ -0,0 +1,78 @@ +import React, {useEffect} from 'react'; +import Animated, {cancelAnimation, Easing, runOnJS, useAnimatedStyle, useSharedValue, withDelay, withRepeat, withSequence, withTiming} from 'react-native-reanimated'; +import useThemeStyles from '@hooks/useThemeStyles'; + +function ProgressBar({shouldShow}: {shouldShow: boolean}) { + const left = useSharedValue(0); + const width = useSharedValue(0); + const opacity = useSharedValue(0); + const isVisible = useSharedValue(false); + const styles = useThemeStyles(); + + useEffect(() => { + if (shouldShow) { + // eslint-disable-next-line react-compiler/react-compiler + isVisible.value = true; + left.value = 0; + width.value = 0; + opacity.value = withTiming(1, {duration: 300}); + left.value = withDelay( + 300, // 0.3s delay + withRepeat( + withSequence( + withTiming(0, {duration: 0}), + withTiming(0, {duration: 750, easing: Easing.bezier(0.65, 0, 0.35, 1)}), + withTiming(100, {duration: 750, easing: Easing.bezier(0.65, 0, 0.35, 1)}), + ), + -1, + false, + ), + ); + + width.value = withDelay( + 300, // 0.3s delay + withRepeat( + withSequence( + withTiming(0, {duration: 0}), + withTiming(100, {duration: 750, easing: Easing.bezier(0.65, 0, 0.35, 1)}), + withTiming(0, {duration: 750, easing: Easing.bezier(0.65, 0, 0.35, 1)}), + ), + -1, + false, + ), + ); + } else if (isVisible.value) { + opacity.value = withTiming(0, {duration: 300}, () => { + runOnJS(() => { + isVisible.value = false; + cancelAnimation(left); + cancelAnimation(width); + }); + }); + } + // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps + }, [shouldShow]); + + const animatedIndicatorStyle = useAnimatedStyle(() => { + return { + left: `${left.value}%`, + width: `${width.value}%`, + }; + }); + + const animatedContainerStyle = useAnimatedStyle(() => { + return { + opacity: opacity.value, + }; + }); + + return isVisible.value ? ( + + + + ) : null; +} + +ProgressBar.displayName = 'ProgressBar'; + +export default ProgressBar; From 0265bc6133f6e93382e98105646ba484831fd173 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 9 Sep 2024 16:53:46 +0700 Subject: [PATCH 003/438] fix style of progress bar --- src/styles/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/styles/index.ts b/src/styles/index.ts index 8a742934739c..825f17701740 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -5202,13 +5202,13 @@ const styles = (theme: ThemeColors) => progressBarWrapper: { height: 2, width: '100%', - backgroundColor: '#1A3D32', + backgroundColor: theme.buttonDefaultBG, borderRadius: 5, overflow: 'hidden', }, progressBar: { height: '100%', - backgroundColor: '#03D47C', + backgroundColor: theme.success, borderRadius: 5, width: '100%', }, From af9b5b8a2f9406d1bf39b01727b3efeb65036bfe Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 9 Sep 2024 17:26:43 +0700 Subject: [PATCH 004/438] fix style progress bar --- src/styles/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/index.ts b/src/styles/index.ts index 825f17701740..b086d4cfd9b3 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -5202,7 +5202,7 @@ const styles = (theme: ThemeColors) => progressBarWrapper: { height: 2, width: '100%', - backgroundColor: theme.buttonDefaultBG, + backgroundColor: theme.border, borderRadius: 5, overflow: 'hidden', }, From c177829d889eb4a1998d524d17c4b453ff82d5bf Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 10 Sep 2024 22:05:34 +0700 Subject: [PATCH 005/438] add new const and fix style progress bar --- src/CONST.ts | 3 +++ src/components/ProgressBar.tsx | 17 +++++++++-------- src/styles/index.ts | 1 - 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index d0695b1e285f..a6578118e7c8 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -180,6 +180,9 @@ const CONST = { ANIMATED_HIGHLIGHT_END_DURATION: 2000, ANIMATED_TRANSITION: 300, ANIMATED_TRANSITION_FROM_VALUE: 100, + ANIMATED_PROGRESS_BAR_DELAY: 300, + ANIMATED_PROGRESS_BAR_OPACITY_DURATION: 300, + ANIMATED_PROGRESS_BAR_DURATION: 750, ANIMATION_IN_TIMING: 100, ANIMATION_DIRECTION: { IN: 'in', diff --git a/src/components/ProgressBar.tsx b/src/components/ProgressBar.tsx index d37d34887c03..63d3d1142d30 100644 --- a/src/components/ProgressBar.tsx +++ b/src/components/ProgressBar.tsx @@ -1,6 +1,7 @@ import React, {useEffect} from 'react'; import Animated, {cancelAnimation, Easing, runOnJS, useAnimatedStyle, useSharedValue, withDelay, withRepeat, withSequence, withTiming} from 'react-native-reanimated'; import useThemeStyles from '@hooks/useThemeStyles'; +import CONST from '@src/CONST'; function ProgressBar({shouldShow}: {shouldShow: boolean}) { const left = useSharedValue(0); @@ -15,14 +16,14 @@ function ProgressBar({shouldShow}: {shouldShow: boolean}) { isVisible.value = true; left.value = 0; width.value = 0; - opacity.value = withTiming(1, {duration: 300}); + opacity.value = withTiming(1, {duration: CONST.ANIMATED_PROGRESS_BAR_OPACITY_DURATION}); left.value = withDelay( - 300, // 0.3s delay + CONST.ANIMATED_PROGRESS_BAR_DELAY, withRepeat( withSequence( withTiming(0, {duration: 0}), - withTiming(0, {duration: 750, easing: Easing.bezier(0.65, 0, 0.35, 1)}), - withTiming(100, {duration: 750, easing: Easing.bezier(0.65, 0, 0.35, 1)}), + withTiming(0, {duration: CONST.ANIMATED_PROGRESS_BAR_DURATION, easing: Easing.bezier(0.65, 0, 0.35, 1)}), + withTiming(100, {duration: CONST.ANIMATED_PROGRESS_BAR_DURATION, easing: Easing.bezier(0.65, 0, 0.35, 1)}), ), -1, false, @@ -30,19 +31,19 @@ function ProgressBar({shouldShow}: {shouldShow: boolean}) { ); width.value = withDelay( - 300, // 0.3s delay + CONST.ANIMATED_PROGRESS_BAR_DELAY, withRepeat( withSequence( withTiming(0, {duration: 0}), - withTiming(100, {duration: 750, easing: Easing.bezier(0.65, 0, 0.35, 1)}), - withTiming(0, {duration: 750, easing: Easing.bezier(0.65, 0, 0.35, 1)}), + withTiming(100, {duration: CONST.ANIMATED_PROGRESS_BAR_DURATION, easing: Easing.bezier(0.65, 0, 0.35, 1)}), + withTiming(0, {duration: CONST.ANIMATED_PROGRESS_BAR_DURATION, easing: Easing.bezier(0.65, 0, 0.35, 1)}), ), -1, false, ), ); } else if (isVisible.value) { - opacity.value = withTiming(0, {duration: 300}, () => { + opacity.value = withTiming(0, {duration: CONST.ANIMATED_PROGRESS_BAR_OPACITY_DURATION}, () => { runOnJS(() => { isVisible.value = false; cancelAnimation(left); diff --git a/src/styles/index.ts b/src/styles/index.ts index b086d4cfd9b3..9221f480ab8e 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -5209,7 +5209,6 @@ const styles = (theme: ThemeColors) => progressBar: { height: '100%', backgroundColor: theme.success, - borderRadius: 5, width: '100%', }, } satisfies Styles); From c7d375175a652019b64c430d7e3f9973e8907f2e Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Thu, 19 Sep 2024 19:01:24 +0200 Subject: [PATCH 006/438] feat: unread marker for messages while offline --- src/hooks/useNetwork.ts | 31 +++++++++++++++++++-- src/pages/home/report/ReportActionsList.tsx | 26 +++++++++++++++-- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/src/hooks/useNetwork.ts b/src/hooks/useNetwork.ts index 950d0592b59c..910f99896d4d 100644 --- a/src/hooks/useNetwork.ts +++ b/src/hooks/useNetwork.ts @@ -1,17 +1,20 @@ -import {useContext, useEffect, useRef} from 'react'; +import {useContext, useEffect, useMemo, useRef, useState} from 'react'; import {NetworkContext} from '@components/OnyxProvider'; +import DateUtils from '@libs/DateUtils'; import CONST from '@src/CONST'; +import useLocalize from './useLocalize'; type UseNetworkProps = { onReconnect?: () => void; }; -type UseNetwork = {isOffline: boolean}; +type UseNetwork = {isOffline: boolean; lastOfflineAt?: Date; lastOnlineAt?: Date}; export default function useNetwork({onReconnect = () => {}}: UseNetworkProps = {}): UseNetwork { const callback = useRef(onReconnect); callback.current = onReconnect; + const {preferredLocale} = useLocalize(); const {isOffline, networkStatus} = useContext(NetworkContext) ?? {...CONST.DEFAULT_NETWORK_DATA, networkStatus: CONST.NETWORK.NETWORK_STATUS.UNKNOWN}; const prevOfflineStatusRef = useRef(isOffline); useEffect(() => { @@ -29,6 +32,28 @@ export default function useNetwork({onReconnect = () => {}}: UseNetworkProps = { prevOfflineStatusRef.current = isOffline; }, [isOffline]); + const isOfflineResult = useMemo(() => (networkStatus === CONST.NETWORK.NETWORK_STATUS.UNKNOWN ? false : isOffline), [isOffline, networkStatus]); + + const [lastOfflineAt, setLastOfflineAt] = useState(() => (isOfflineResult ? DateUtils.getLocalDateFromDatetime(preferredLocale) : undefined)); + useEffect(() => { + if (!isOffline) { + return; + } + setLastOfflineAt(DateUtils.getLocalDateFromDatetime(preferredLocale)); + // eslint-disable-next-line react-compiler/react-compiler + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isOffline]); + + const [lastOnlineAt, setLastOnlineAt] = useState(() => (isOfflineResult ? undefined : DateUtils.getLocalDateFromDatetime(preferredLocale))); + useEffect(() => { + if (isOffline) { + return; + } + setLastOnlineAt(DateUtils.getLocalDateFromDatetime(preferredLocale)); + // eslint-disable-next-line react-compiler/react-compiler + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isOffline]); + // If the network status is undefined, we don't treat it as offline. Otherwise, we utilize the isOffline prop. - return {isOffline: networkStatus === CONST.NETWORK.NETWORK_STATUS.UNKNOWN ? false : isOffline}; + return {isOffline: isOfflineResult, lastOfflineAt, lastOnlineAt}; } diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index 6828e10e7e3b..ed44677ad1c5 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -122,6 +122,19 @@ function keyExtractor(item: OnyxTypes.ReportAction): string { return item.reportActionID; } +function wasMessageReceivedWhileOffline(message: OnyxTypes.ReportAction, offlineLastAt: Date | undefined, onlineLastAt: Date | undefined, locale: OnyxTypes.Locale): boolean { + if (!onlineLastAt || !offlineLastAt) { + return false; + } + + const messageCreatedAt = DateUtils.getLocalDateFromDatetime(locale, message.created); + + if (messageCreatedAt > offlineLastAt && messageCreatedAt <= onlineLastAt) { + return true; + } + return false; +} + function isMessageUnread(message: OnyxTypes.ReportAction, lastReadTime?: string): boolean { if (!lastReadTime) { return !ReportActionsUtils.isCreatedAction(message); @@ -161,7 +174,8 @@ function ReportActionsList({ const {windowHeight} = useWindowDimensions(); const {shouldUseNarrowLayout} = useResponsiveLayout(); - const {isOffline} = useNetwork(); + const {preferredLocale} = useLocalize(); + const {isOffline, lastOfflineAt, lastOnlineAt} = useNetwork(); const route = useRoute>(); const reportScrollManager = useReportScrollManager(); const userActiveSince = useRef(DateUtils.getDBTime()); @@ -222,6 +236,14 @@ function ReportActionsList({ const unreadMarkerReportActionID = useMemo(() => { const shouldDisplayNewMarker = (reportAction: OnyxTypes.ReportAction, index: number): boolean => { const nextMessage = sortedVisibleReportActions[index + 1]; + + const isCurrentMessageOffline = wasMessageReceivedWhileOffline(reportAction, lastOfflineAt, lastOnlineAt, preferredLocale); + const isNextMessageOffline = (nextMessage && wasMessageReceivedWhileOffline(nextMessage, lastOfflineAt, lastOnlineAt, preferredLocale)) || !nextMessage; + + if (isCurrentMessageOffline && !isNextMessageOffline) { + return true; + } + const isCurrentMessageUnread = isMessageUnread(reportAction, unreadMarkerTime); const isNextMessageRead = !nextMessage || !isMessageUnread(nextMessage, unreadMarkerTime); let shouldDisplay = isCurrentMessageUnread && isNextMessageRead && !ReportActionsUtils.shouldHideNewMarker(reportAction); @@ -245,7 +267,7 @@ function ReportActionsList({ } return null; - }, [accountID, sortedVisibleReportActions, unreadMarkerTime, messageManuallyMarkedUnread]); + }, [sortedVisibleReportActions, lastOfflineAt, lastOnlineAt, preferredLocale, unreadMarkerTime, messageManuallyMarkedUnread, accountID]); /** * Subscribe to read/unread events and update our unreadMarkerTime From 3c91994a84f00a7b00df6ff81e801d4d8f7f5d02 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Sat, 21 Sep 2024 13:47:17 +0700 Subject: [PATCH 007/438] Calculate routes for pending transaction backup --- src/CONST.ts | 6 + src/hooks/useFetchRoute.ts | 7 +- src/libs/API/types.ts | 1 + src/libs/actions/Transaction.ts | 268 +++++++++++++++++- .../step/IOURequestStepConfirmation.tsx | 2 +- .../request/step/IOURequestStepDistance.tsx | 7 +- src/types/utils/TransactionStateType.ts | 6 + 7 files changed, 285 insertions(+), 12 deletions(-) create mode 100644 src/types/utils/TransactionStateType.ts diff --git a/src/CONST.ts b/src/CONST.ts index 5a0c6f395eb4..87ca20a5d020 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -5775,6 +5775,12 @@ const CONST = { REPORT_ACTIONS: 'actions', REPORT_ACTION_PREVIEW: 'preview', }, + + TRANSACTION_STATE: { + CURRENT: 'current', + DRAFT: 'draft', + BACKUP: 'backup', + } } as const; type Country = keyof typeof CONST.ALL_COUNTRIES; diff --git a/src/hooks/useFetchRoute.ts b/src/hooks/useFetchRoute.ts index 4222f9f0183c..650734b68dc3 100644 --- a/src/hooks/useFetchRoute.ts +++ b/src/hooks/useFetchRoute.ts @@ -1,7 +1,6 @@ import isEqual from 'lodash/isEqual'; import {useEffect} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; -import * as IOUUtils from '@libs/IOUUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import * as TransactionAction from '@userActions/Transaction'; import type {IOUAction} from '@src/CONST'; @@ -9,8 +8,10 @@ import type {Transaction} from '@src/types/onyx'; import type {WaypointCollection} from '@src/types/onyx/Transaction'; import useNetwork from './useNetwork'; import usePrevious from './usePrevious'; +import TransactionState from '@src/types/utils/TransactionStateType'; +import CONST from '@src/CONST'; -export default function useFetchRoute(transaction: OnyxEntry, waypoints: WaypointCollection | undefined, action: IOUAction) { +export default function useFetchRoute(transaction: OnyxEntry, waypoints: WaypointCollection | undefined, action: IOUAction, transactionState: TransactionState = CONST.TRANSACTION_STATE.CURRENT) { const {isOffline} = useNetwork(); const hasRouteError = !!transaction?.errorFields?.route; const hasRoute = TransactionUtils.hasRoute(transaction); @@ -27,7 +28,7 @@ export default function useFetchRoute(transaction: OnyxEntry, waypo return; } - TransactionAction.getRoute(transaction.transactionID, validatedWaypoints, IOUUtils.shouldUseTransactionDraft(action)); + TransactionAction.getRoute(transaction.transactionID, validatedWaypoints, transactionState); }, [shouldFetchRoute, transaction?.transactionID, validatedWaypoints, isOffline, action]); return {shouldFetchRoute, validatedWaypoints}; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index 37bdf6b81d6e..1976cce2d13a 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -842,6 +842,7 @@ const READ_COMMANDS = { SEND_PERFORMANCE_TIMING: 'SendPerformanceTiming', GET_ROUTE: 'GetRoute', GET_ROUTE_FOR_DRAFT: 'GetRouteForDraft', + GET_ROUTE_FOR_BACKUP: 'GetRouteForBackup', GET_STATEMENT_PDF: 'GetStatementPDF', OPEN_ONFIDO_FLOW: 'OpenOnfidoFlow', OPEN_INITIAL_SETTINGS_PAGE: 'OpenInitialSettingsPage', diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index e19251b62ce8..506c9a6ece4c 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -16,6 +16,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetails, RecentWaypoint, ReportAction, ReportActions, ReviewDuplicates, Transaction, TransactionViolation, TransactionViolations} from '@src/types/onyx'; import type {OnyxData} from '@src/types/onyx/Request'; import type {WaypointCollection} from '@src/types/onyx/Transaction'; +import type TransactionState from '@src/types/utils/TransactionStateType'; let recentWaypoints: RecentWaypoint[] = []; Onyx.connect({ @@ -199,13 +200,27 @@ function removeWaypoint(transaction: OnyxEntry, currentIndex: strin return Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction?.transactionID}`, newTransaction); } -function getOnyxDataForRouteRequest(transactionID: string, isDraft = false): OnyxData { +function getOnyxDataForRouteRequest(transactionID: string, transactionState: TransactionState = CONST.TRANSACTION_STATE.CURRENT): OnyxData { + let keyPrefix; + switch (transactionState) { + case CONST.TRANSACTION_STATE.DRAFT: + keyPrefix = ONYXKEYS.COLLECTION.TRANSACTION_DRAFT; + break; + case CONST.TRANSACTION_STATE.BACKUP: + keyPrefix = ONYXKEYS.COLLECTION.TRANSACTION_BACKUP; + break; + case CONST.TRANSACTION_STATE.CURRENT: + default: + keyPrefix = ONYXKEYS.COLLECTION.TRANSACTION; + break; + } + return { optimisticData: [ { // Clears any potentially stale error messages from fetching the route onyxMethod: Onyx.METHOD.MERGE, - key: `${isDraft ? ONYXKEYS.COLLECTION.TRANSACTION_DRAFT : ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, + key: `${keyPrefix}${transactionID}`, value: { comment: { isLoading: true, @@ -220,7 +235,7 @@ function getOnyxDataForRouteRequest(transactionID: string, isDraft = false): Ony successData: [ { onyxMethod: Onyx.METHOD.MERGE, - key: `${isDraft ? ONYXKEYS.COLLECTION.TRANSACTION_DRAFT : ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, + key: `${keyPrefix}${transactionID}`, value: { comment: { isLoading: false, @@ -231,7 +246,7 @@ function getOnyxDataForRouteRequest(transactionID: string, isDraft = false): Ony failureData: [ { onyxMethod: Onyx.METHOD.MERGE, - key: `${isDraft ? ONYXKEYS.COLLECTION.TRANSACTION_DRAFT : ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, + key: `${keyPrefix}${transactionID}`, value: { comment: { isLoading: false, @@ -242,19 +257,258 @@ function getOnyxDataForRouteRequest(transactionID: string, isDraft = false): Ony }; } +function mockGetBackupRoute(transactionID: string) { + Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_BACKUP}${transactionID}`, { + "routes": { + "route0": { + "distance": 154384.844, + "geometry": { + "coordinates": [ + [ + 107.61964, + -6.917596 + ], + [ + 107.621458, + -6.918055 + ], + [ + 107.621155, + -6.919264 + ], + [ + 107.621862, + -6.92007 + ], + [ + 107.617256, + -6.922397 + ], + [ + 107.586943, + -6.918774 + ], + [ + 107.585588, + -6.926812 + ], + [ + 107.552806, + -6.938898 + ], + [ + 107.541552, + -6.912423 + ], + [ + 107.507715, + -6.867412 + ], + [ + 107.504802, + -6.856305 + ], + [ + 107.50362, + -6.841229 + ], + [ + 107.471934, + -6.809238 + ], + [ + 107.447898, + -6.798421 + ], + [ + 107.439988, + -6.779357 + ], + [ + 107.440242, + -6.74621 + ], + [ + 107.431506, + -6.708104 + ], + [ + 107.442126, + -6.666515 + ], + [ + 107.427061, + -6.653178 + ], + [ + 107.428849, + -6.642103 + ], + [ + 107.421426, + -6.611517 + ], + [ + 107.434251, + -6.579176 + ], + [ + 107.424954, + -6.559044 + ], + [ + 107.446783, + -6.484271 + ], + [ + 107.444693, + -6.467799 + ], + [ + 107.426815, + -6.442001 + ], + [ + 107.424102, + -6.423177 + ], + [ + 107.402604, + -6.392909 + ], + [ + 107.334314, + -6.355931 + ], + [ + 107.277154, + -6.349437 + ], + [ + 107.256766, + -6.349048 + ], + [ + 107.235902, + -6.355155 + ], + [ + 107.206996, + -6.349584 + ], + [ + 107.099379, + -6.29197 + ], + [ + 106.981406, + -6.249348 + ], + [ + 106.957583, + -6.256527 + ], + [ + 106.916207, + -6.258484 + ], + [ + 106.889886, + -6.244657 + ], + [ + 106.880764, + -6.247631 + ], + [ + 106.833371, + -6.240614 + ], + [ + 106.815565, + -6.224157 + ], + [ + 106.811859, + -6.219031 + ], + [ + 106.817977, + -6.214838 + ], + [ + 106.822696, + -6.195377 + ], + [ + 106.822922, + -6.194464 + ] + ], + "type": "LineString" + } + } + }, + "comment": { + "waypoints": { + "waypoint0": { + "keyForList": "Bandung_1726892371069", + "lat": -6.9174639, + "lng": 107.6191228, + "address": "Bandung, Bandung City, West Java, Indonesia", + "name": "Bandung" + }, + "waypoint1": { + "keyForList": "Jakarta_1726892375416", + "lat": -6.1944491, + "lng": 106.8229198, + "address": "Jakarta, Indonesia", + "name": "Jakarta" + } + }, + "customUnit": { + "quantity": 154384.844 + } + } + } + ) +} + + /** * Gets the route for a set of waypoints * Used so we can generate a map view of the provided waypoints */ -function getRoute(transactionID: string, waypoints: WaypointCollection, isDraft: boolean) { + + +function getRoute(transactionID: string, waypoints: WaypointCollection, routeType: TransactionState = CONST.TRANSACTION_STATE.CURRENT) { + if (routeType === CONST.TRANSACTION_STATE.BACKUP) + { + mockGetBackupRoute(transactionID); + } + const parameters: GetRouteParams = { transactionID, waypoints: JSON.stringify(waypoints), }; - API.read(isDraft ? READ_COMMANDS.GET_ROUTE_FOR_DRAFT : READ_COMMANDS.GET_ROUTE, parameters, getOnyxDataForRouteRequest(transactionID, isDraft)); -} + let command; + switch (routeType) { + case 'draft': + command = READ_COMMANDS.GET_ROUTE_FOR_DRAFT; + break; + case 'current': + command = READ_COMMANDS.GET_ROUTE; + break; + case 'backup': + command = READ_COMMANDS.GET_ROUTE_FOR_BACKUP; + break; + default: + throw new Error('Invalid route type'); + } + API.read(command, parameters, getOnyxDataForRouteRequest(transactionID, routeType === 'draft')); +} /** * Updates all waypoints stored in the transaction specified by the provided transactionID. * diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 6c1457abef62..5c7d697c01be 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -161,7 +161,7 @@ function IOURequestStepConfirmation({ const isPolicyExpenseChat = useMemo(() => participants?.some((participant) => participant.isPolicyExpenseChat), [participants]); const formHasBeenSubmitted = useRef(false); - useFetchRoute(transaction, transaction?.comment?.waypoints, action); + useFetchRoute(transaction, transaction?.comment?.waypoints, action, IOUUtils.shouldUseTransactionDraft(action) ? CONST.TRANSACTION_STATE.DRAFT : CONST.TRANSACTION_STATE.CURRENT); useEffect(() => { const policyExpenseChat = participants?.find((participant) => participant.isPolicyExpenseChat); diff --git a/src/pages/iou/request/step/IOURequestStepDistance.tsx b/src/pages/iou/request/step/IOURequestStepDistance.tsx index 14597df8e313..12d2cf375ca1 100644 --- a/src/pages/iou/request/step/IOURequestStepDistance.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistance.tsx @@ -90,7 +90,12 @@ function IOURequestStepDistance({ }, [optimisticWaypoints, transaction], ); - const {shouldFetchRoute, validatedWaypoints} = useFetchRoute(transaction, waypoints, action); + + const backupWaypoints = !!transactionBackup?.pendingFields?.waypoints ? transactionBackup?.comment?.waypoints : undefined; + + const { shouldFetchRoute, validatedWaypoints } = useFetchRoute(transaction, waypoints, action, IOUUtils.shouldUseTransactionDraft(action) ? CONST.TRANSACTION_STATE.DRAFT : CONST.TRANSACTION_STATE.CURRENT); + useFetchRoute(transactionBackup, backupWaypoints, action, CONST.TRANSACTION_STATE.BACKUP); + const waypointsList = Object.keys(waypoints); const previousWaypoints = usePrevious(waypoints); const numberOfWaypoints = Object.keys(waypoints).length; diff --git a/src/types/utils/TransactionStateType.ts b/src/types/utils/TransactionStateType.ts new file mode 100644 index 000000000000..81c1438d873c --- /dev/null +++ b/src/types/utils/TransactionStateType.ts @@ -0,0 +1,6 @@ +import type {ValueOf} from 'type-fest'; +import type CONST from '@src/CONST'; + +type TransactionStateType = ValueOf; + +export default TransactionStateType; \ No newline at end of file From 5f511d271bcf33beddf4c439d5f9454e2df4d41a Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Sat, 21 Sep 2024 15:44:00 +0700 Subject: [PATCH 008/438] fix incorrect param input --- src/libs/actions/Transaction.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index 506c9a6ece4c..1222e6455770 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -507,7 +507,7 @@ function getRoute(transactionID: string, waypoints: WaypointCollection, routeTyp throw new Error('Invalid route type'); } - API.read(command, parameters, getOnyxDataForRouteRequest(transactionID, routeType === 'draft')); + API.read(command, parameters, getOnyxDataForRouteRequest(transactionID, routeType)); } /** * Updates all waypoints stored in the transaction specified by the provided transactionID. From a53236d45b59a374fb0dfd43f879a6e4456dcba9 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Sat, 21 Sep 2024 16:05:33 +0700 Subject: [PATCH 009/438] return if use mock data --- src/libs/actions/Transaction.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index 1222e6455770..26eae38d2588 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -482,9 +482,11 @@ function mockGetBackupRoute(transactionID: string) { function getRoute(transactionID: string, waypoints: WaypointCollection, routeType: TransactionState = CONST.TRANSACTION_STATE.CURRENT) { + /** For testing, remove when new API endpoint ready, waypoints: Bandung, Jakarta */ if (routeType === CONST.TRANSACTION_STATE.BACKUP) { mockGetBackupRoute(transactionID); + return; } const parameters: GetRouteParams = { From 6d4ff5a05d95a004904fad4fafb4baaf35132f48 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Sat, 21 Sep 2024 20:55:59 +0200 Subject: [PATCH 010/438] add comments --- src/hooks/useNetwork.ts | 4 +++- src/pages/home/report/ReportActionsList.tsx | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/hooks/useNetwork.ts b/src/hooks/useNetwork.ts index 910f99896d4d..c95c86edb9fb 100644 --- a/src/hooks/useNetwork.ts +++ b/src/hooks/useNetwork.ts @@ -32,8 +32,10 @@ export default function useNetwork({onReconnect = () => {}}: UseNetworkProps = { prevOfflineStatusRef.current = isOffline; }, [isOffline]); + // If the network status is undefined, we don't treat it as offline. Otherwise, we utilize the isOffline prop. const isOfflineResult = useMemo(() => (networkStatus === CONST.NETWORK.NETWORK_STATUS.UNKNOWN ? false : isOffline), [isOffline, networkStatus]); + // Used to get the last time the user went offline const [lastOfflineAt, setLastOfflineAt] = useState(() => (isOfflineResult ? DateUtils.getLocalDateFromDatetime(preferredLocale) : undefined)); useEffect(() => { if (!isOffline) { @@ -44,6 +46,7 @@ export default function useNetwork({onReconnect = () => {}}: UseNetworkProps = { // eslint-disable-next-line react-hooks/exhaustive-deps }, [isOffline]); + // Used to get the last time the user went back online after being offline. const [lastOnlineAt, setLastOnlineAt] = useState(() => (isOfflineResult ? undefined : DateUtils.getLocalDateFromDatetime(preferredLocale))); useEffect(() => { if (isOffline) { @@ -54,6 +57,5 @@ export default function useNetwork({onReconnect = () => {}}: UseNetworkProps = { // eslint-disable-next-line react-hooks/exhaustive-deps }, [isOffline]); - // If the network status is undefined, we don't treat it as offline. Otherwise, we utilize the isOffline prop. return {isOffline: isOfflineResult, lastOfflineAt, lastOnlineAt}; } diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index b8e06b08cc42..2281b3798d5c 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -238,9 +238,9 @@ function ReportActionsList({ const shouldDisplayNewMarker = (reportAction: OnyxTypes.ReportAction, index: number): boolean => { const nextMessage = sortedVisibleReportActions[index + 1]; + // If the user recevied new messages while being offline, we want to display the unread marker above the first offline message. const isCurrentMessageOffline = wasMessageReceivedWhileOffline(reportAction, lastOfflineAt, lastOnlineAt, preferredLocale); const isNextMessageOffline = (nextMessage && wasMessageReceivedWhileOffline(nextMessage, lastOfflineAt, lastOnlineAt, preferredLocale)) || !nextMessage; - if (isCurrentMessageOffline && !isNextMessageOffline) { return true; } From 077ba40d0393e364a60731b23dce13aefca16291 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Sat, 21 Sep 2024 20:58:20 +0200 Subject: [PATCH 011/438] add comment and fix logic --- src/hooks/useNetwork.ts | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/hooks/useNetwork.ts b/src/hooks/useNetwork.ts index c95c86edb9fb..5a686b5ab138 100644 --- a/src/hooks/useNetwork.ts +++ b/src/hooks/useNetwork.ts @@ -15,28 +15,29 @@ export default function useNetwork({onReconnect = () => {}}: UseNetworkProps = { callback.current = onReconnect; const {preferredLocale} = useLocalize(); - const {isOffline, networkStatus} = useContext(NetworkContext) ?? {...CONST.DEFAULT_NETWORK_DATA, networkStatus: CONST.NETWORK.NETWORK_STATUS.UNKNOWN}; - const prevOfflineStatusRef = useRef(isOffline); + const {isOffline: isOfflineContext, networkStatus} = useContext(NetworkContext) ?? {...CONST.DEFAULT_NETWORK_DATA, networkStatus: CONST.NETWORK.NETWORK_STATUS.UNKNOWN}; + const prevOfflineStatusRef = useRef(isOfflineContext); useEffect(() => { // If we were offline before and now we are not offline then we just reconnected - const didReconnect = prevOfflineStatusRef.current && !isOffline; + const didReconnect = prevOfflineStatusRef.current && !isOfflineContext; if (!didReconnect) { return; } callback.current(); - }, [isOffline]); + }, [isOfflineContext]); useEffect(() => { // Used to store previous prop values to compare on next render - prevOfflineStatusRef.current = isOffline; - }, [isOffline]); + prevOfflineStatusRef.current = isOfflineContext; + }, [isOfflineContext]); // If the network status is undefined, we don't treat it as offline. Otherwise, we utilize the isOffline prop. - const isOfflineResult = useMemo(() => (networkStatus === CONST.NETWORK.NETWORK_STATUS.UNKNOWN ? false : isOffline), [isOffline, networkStatus]); + const isOffline = useMemo(() => (networkStatus === CONST.NETWORK.NETWORK_STATUS.UNKNOWN ? false : isOfflineContext), [isOfflineContext, networkStatus]); - // Used to get the last time the user went offline - const [lastOfflineAt, setLastOfflineAt] = useState(() => (isOfflineResult ? DateUtils.getLocalDateFromDatetime(preferredLocale) : undefined)); + // Used to get the last time the user went offline. + // Set to a JS Date object if the user was offline before, otherwise undefined. + const [lastOfflineAt, setLastOfflineAt] = useState(() => (isOffline ? DateUtils.getLocalDateFromDatetime(preferredLocale) : undefined)); useEffect(() => { if (!isOffline) { return; @@ -47,7 +48,8 @@ export default function useNetwork({onReconnect = () => {}}: UseNetworkProps = { }, [isOffline]); // Used to get the last time the user went back online after being offline. - const [lastOnlineAt, setLastOnlineAt] = useState(() => (isOfflineResult ? undefined : DateUtils.getLocalDateFromDatetime(preferredLocale))); + // Set to a JS Date object if the user was online before, otherwise undefined. + const [lastOnlineAt, setLastOnlineAt] = useState(() => (isOffline ? undefined : DateUtils.getLocalDateFromDatetime(preferredLocale))); useEffect(() => { if (isOffline) { return; @@ -57,5 +59,5 @@ export default function useNetwork({onReconnect = () => {}}: UseNetworkProps = { // eslint-disable-next-line react-hooks/exhaustive-deps }, [isOffline]); - return {isOffline: isOfflineResult, lastOfflineAt, lastOnlineAt}; + return {isOffline, lastOfflineAt, lastOnlineAt}; } From 7b81e1b08a1fe42eb2380b3858289f9bdbfa720b Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Wed, 25 Sep 2024 18:45:14 +0200 Subject: [PATCH 012/438] save optimistic report --- src/libs/actions/Report.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index b038f16d003d..e423e3c082b4 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1363,6 +1363,7 @@ function handleReportChanged(report: OnyxEntry) { if (report?.reportID && report.preexistingReportID) { let callback = () => { Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`, null); + Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${report.preexistingReportID}`, {...report, reportID: report.preexistingReportID, preexistingReportID: null}); Onyx.set(`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${report.reportID}`, null); }; // Only re-route them if they are still looking at the optimistically created report From 478848ba2484724d682e404b3912df47fd64c614 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Wed, 25 Sep 2024 18:46:09 +0200 Subject: [PATCH 013/438] skip openReport for preexistingReportID --- src/pages/home/ReportScreen.tsx | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index b8b551a345ca..73187f10a67b 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -4,7 +4,7 @@ import type {StackScreenProps} from '@react-navigation/stack'; import lodashIsEqual from 'lodash/isEqual'; import React, {memo, useCallback, useEffect, useMemo, useRef, useState} from 'react'; import type {FlatList, ViewStyle} from 'react-native'; -import {InteractionManager, View} from 'react-native'; +import {DeviceEventEmitter, InteractionManager, View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {useOnyx} from 'react-native-onyx'; import Banner from '@components/Banner'; @@ -104,6 +104,7 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro const isFocused = useIsFocused(); const prevIsFocused = usePrevious(isFocused); const firstRenderRef = useRef(true); + const isSkippingOpenReport = useRef(false); const flatListRef = useRef(null); const {canUseDefaultRooms} = usePermissions(); const reactionListRef = useRef(null); @@ -421,6 +422,19 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro Report.updateLastVisitTime(reportID); }, [reportID, isFocused]); + useEffect(() => { + const skipOpenReportListener = DeviceEventEmitter.addListener(`switchToPreExistingReport_${reportID}`, ({preexistingReportID}: {preexistingReportID: string}) => { + if (!preexistingReportID) { + return; + } + isSkippingOpenReport.current = true; + }); + + return () => { + skipOpenReportListener.remove(); + }; + }, [reportID]); + const fetchReportIfNeeded = useCallback(() => { // Report ID will be empty when the reports collection is empty. // This could happen when we are loading the collection for the first time after logging in. @@ -443,6 +457,12 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro if (report && !shouldFetchReport(report) && (isInitialPageReady || isLinkedMessagePageReady)) { return; } + // When creating an optimistic report that already exists, we need to skip openReport + // when replacing the optimistic report with the real one received from the server. + if (isSkippingOpenReport.current) { + isSkippingOpenReport.current = false; + return; + } fetchReport(); }, [report, fetchReport, reportIDFromRoute, isLoadingApp, isInitialPageReady, isLinkedMessagePageReady]); From 3c4ad0295b385450201e304b6a89d8b67178ec98 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Thu, 26 Sep 2024 16:39:45 +0200 Subject: [PATCH 014/438] fix: don't show unread marker for own messages --- src/pages/home/report/ReportActionsList.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index 75936411b904..72ef3c459871 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -236,10 +236,12 @@ function ReportActionsList({ const nextMessage = sortedVisibleReportActions[index + 1]; // If the user recevied new messages while being offline, we want to display the unread marker above the first offline message. - const isCurrentMessageOffline = wasMessageReceivedWhileOffline(reportAction, lastOfflineAt, lastOnlineAt, preferredLocale); - const isNextMessageOffline = (nextMessage && wasMessageReceivedWhileOffline(nextMessage, lastOfflineAt, lastOnlineAt, preferredLocale)) || !nextMessage; - if (isCurrentMessageOffline && !isNextMessageOffline) { - return true; + if (!ReportActionsUtils.wasActionTakenByCurrentUser(reportAction)) { + const isCurrentMessageOffline = wasMessageReceivedWhileOffline(reportAction, lastOfflineAt, lastOnlineAt, preferredLocale); + const isNextMessageOffline = (nextMessage && wasMessageReceivedWhileOffline(nextMessage, lastOfflineAt, lastOnlineAt, preferredLocale)) || !nextMessage; + if (isCurrentMessageOffline && !isNextMessageOffline) { + return true; + } } const isCurrentMessageUnread = isMessageUnread(reportAction, unreadMarkerTime); From 32117b20050e7bce79e80d93ff9cb0cf0a87d153 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 30 Sep 2024 16:02:17 +0700 Subject: [PATCH 015/438] fix style progress bar --- src/components/ProgressBar.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/ProgressBar.tsx b/src/components/ProgressBar.tsx index 63d3d1142d30..885fe337c002 100644 --- a/src/components/ProgressBar.tsx +++ b/src/components/ProgressBar.tsx @@ -67,11 +67,11 @@ function ProgressBar({shouldShow}: {shouldShow: boolean}) { }; }); - return isVisible.value ? ( + return ( - + {isVisible.value ? : null} - ) : null; + ); } ProgressBar.displayName = 'ProgressBar'; From 744e54c3f0aa2bc58949b09903aa179e19e1a62d Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Tue, 1 Oct 2024 15:23:05 +0200 Subject: [PATCH 016/438] re-structure offline message detection logic --- src/pages/home/report/ReportActionsList.tsx | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index 3d0afb393806..e29abe64f90a 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -228,26 +228,31 @@ function ReportActionsList({ // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, [report.reportID]); + const isMessageOffline = useCallback( + (m: OnyxTypes.ReportAction) => wasMessageReceivedWhileOffline(m, lastOfflineAt, lastOnlineAt, preferredLocale), + [lastOfflineAt, lastOnlineAt, preferredLocale], + ); + /** * The reportActionID the unread marker should display above */ const unreadMarkerReportActionID = useMemo(() => { - const shouldDisplayNewMarker = (reportAction: OnyxTypes.ReportAction, index: number): boolean => { + const shouldDisplayNewMarker = (message: OnyxTypes.ReportAction, index: number): boolean => { const nextMessage = sortedVisibleReportActions[index + 1]; // If the user recevied new messages while being offline, we want to display the unread marker above the first offline message. - if (!ReportActionsUtils.wasActionTakenByCurrentUser(reportAction)) { - const isCurrentMessageOffline = wasMessageReceivedWhileOffline(reportAction, lastOfflineAt, lastOnlineAt, preferredLocale); - const isNextMessageOffline = (nextMessage && wasMessageReceivedWhileOffline(nextMessage, lastOfflineAt, lastOnlineAt, preferredLocale)) || !nextMessage; + if (!ReportActionsUtils.wasActionTakenByCurrentUser(message)) { + const isCurrentMessageOffline = isMessageOffline(message); + const isNextMessageOffline = nextMessage && !ReportActionsUtils.wasActionTakenByCurrentUser(nextMessage) && isMessageOffline(nextMessage); if (isCurrentMessageOffline && !isNextMessageOffline) { return true; } } - const isCurrentMessageUnread = isMessageUnread(reportAction, unreadMarkerTime); + const isCurrentMessageUnread = isMessageUnread(message, unreadMarkerTime); const isNextMessageRead = !nextMessage || !isMessageUnread(nextMessage, unreadMarkerTime); - const shouldDisplay = isCurrentMessageUnread && isNextMessageRead && !ReportActionsUtils.shouldHideNewMarker(reportAction); - const isWithinVisibleThreshold = scrollingVerticalOffset.current < MSG_VISIBLE_THRESHOLD ? reportAction.created < (userActiveSince.current ?? '') : true; + const shouldDisplay = isCurrentMessageUnread && isNextMessageRead && !ReportActionsUtils.shouldHideNewMarker(message); + const isWithinVisibleThreshold = scrollingVerticalOffset.current < MSG_VISIBLE_THRESHOLD ? message.created < (userActiveSince.current ?? '') : true; return shouldDisplay && isWithinVisibleThreshold; }; @@ -260,7 +265,7 @@ function ReportActionsList({ } return null; - }, [sortedVisibleReportActions, lastOfflineAt, lastOnlineAt, preferredLocale, unreadMarkerTime]); + }, [sortedVisibleReportActions, unreadMarkerTime, isMessageOffline]); /** * Subscribe to read/unread events and update our unreadMarkerTime From 1709905f776da09c567cc76349763a429de55406 Mon Sep 17 00:00:00 2001 From: Shahidullah Muffakir Date: Tue, 8 Oct 2024 22:42:35 +0530 Subject: [PATCH 017/438] migrate from withOnyx HOC to the useOnyx hook. --- src/components/MapView/MapView.tsx | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/components/MapView/MapView.tsx b/src/components/MapView/MapView.tsx index cbe604661c05..a72788fcc62f 100644 --- a/src/components/MapView/MapView.tsx +++ b/src/components/MapView/MapView.tsx @@ -3,7 +3,7 @@ import type {MapState} from '@rnmapbox/maps'; import Mapbox, {MarkerView, setAccessToken} from '@rnmapbox/maps'; import {forwardRef, memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; import {View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import Button from '@components/Button'; import * as Expensicons from '@components/Icon/Expensicons'; import useTheme from '@hooks/useTheme'; @@ -21,11 +21,12 @@ import Direction from './Direction'; import type {MapViewHandle} from './MapViewTypes'; import PendingMapView from './PendingMapView'; import responder from './responder'; -import type {ComponentProps, MapViewOnyxProps} from './types'; +import type {ComponentProps} from './types'; import utils from './utils'; const MapView = forwardRef( - ({accessToken, style, mapPadding, userLocation, styleURL, pitchEnabled, initialState, waypoints, directionCoordinates, onMapReady, interactive = true}, ref) => { + ({accessToken, style, mapPadding, styleURL, pitchEnabled, initialState, waypoints, directionCoordinates, onMapReady, interactive = true}, ref) => { + const [userLocation] = useOnyx(ONYXKEYS.USER_LOCATION); const navigation = useNavigation(); const {isOffline} = useNetwork(); const {translate} = useLocalize(); @@ -298,8 +299,4 @@ const MapView = forwardRef( }, ); -export default withOnyx({ - userLocation: { - key: ONYXKEYS.USER_LOCATION, - }, -})(memo(MapView)); +export default memo(MapView); From 4bef7003eb8a3655663e3a63f428462db50efd1e Mon Sep 17 00:00:00 2001 From: Shahidullah Muffakir Date: Tue, 8 Oct 2024 23:02:04 +0530 Subject: [PATCH 018/438] remove userLocation from Props types and migrate from withOnyx HOC to useOnyx hook --- src/components/MapView/MapViewImpl.website.tsx | 13 +++++-------- src/components/MapView/types.ts | 8 ++------ 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/components/MapView/MapViewImpl.website.tsx b/src/components/MapView/MapViewImpl.website.tsx index 7df277671043..5958b2939396 100644 --- a/src/components/MapView/MapViewImpl.website.tsx +++ b/src/components/MapView/MapViewImpl.website.tsx @@ -9,7 +9,7 @@ import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, import type {MapRef, ViewState} from 'react-map-gl'; import Map, {Marker} from 'react-map-gl'; import {View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import Button from '@components/Button'; import * as Expensicons from '@components/Icon/Expensicons'; import usePrevious from '@hooks/usePrevious'; @@ -29,7 +29,7 @@ import './mapbox.css'; import type {MapViewHandle} from './MapViewTypes'; import PendingMapView from './PendingMapView'; import responder from './responder'; -import type {ComponentProps, MapViewOnyxProps} from './types'; +import type {ComponentProps} from './types'; import utils from './utils'; const MapViewImpl = forwardRef( @@ -40,13 +40,14 @@ const MapViewImpl = forwardRef( waypoints, mapPadding, accessToken, - userLocation, directionCoordinates, initialState = {location: CONST.MAPBOX.DEFAULT_COORDINATE, zoom: CONST.MAPBOX.DEFAULT_ZOOM}, interactive = true, }, ref, ) => { + const [userLocation] = useOnyx(ONYXKEYS.USER_LOCATION); + const {isOffline} = useNetwork(); const {translate} = useLocalize(); @@ -295,8 +296,4 @@ const MapViewImpl = forwardRef( }, ); -export default withOnyx({ - userLocation: { - key: ONYXKEYS.USER_LOCATION, - }, -})(MapViewImpl); +export default MapViewImpl; diff --git a/src/components/MapView/types.ts b/src/components/MapView/types.ts index a0494a9ac499..77b1b9eb82c2 100644 --- a/src/components/MapView/types.ts +++ b/src/components/MapView/types.ts @@ -2,10 +2,6 @@ import type {OnyxEntry} from 'react-native-onyx'; import type * as OnyxTypes from '@src/types/onyx'; import type {MapViewProps} from './MapViewTypes'; -type MapViewOnyxProps = { - userLocation: OnyxEntry; -}; +type ComponentProps = MapViewProps; -type ComponentProps = MapViewProps & MapViewOnyxProps; - -export type {MapViewOnyxProps, ComponentProps}; +export type {ComponentProps}; From e7f3b6e14bf2044b274d44dfb24125f7ff2d0ca5 Mon Sep 17 00:00:00 2001 From: Shahidullah Muffakir Date: Tue, 8 Oct 2024 23:19:45 +0530 Subject: [PATCH 019/438] Address TypeScript errors --- src/components/MapView/MapView.website.tsx | 5 ++--- src/components/MapView/MapViewImpl.website.tsx | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/components/MapView/MapView.website.tsx b/src/components/MapView/MapView.website.tsx index 3a28943b575a..52cdaee902a3 100644 --- a/src/components/MapView/MapView.website.tsx +++ b/src/components/MapView/MapView.website.tsx @@ -4,11 +4,10 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import usePrevious from '@hooks/usePrevious'; import useThemeStyles from '@hooks/useThemeStyles'; -import type {MapViewHandle} from './MapViewTypes'; +import type {MapViewHandle, MapViewProps} from './MapViewTypes'; import PendingMapView from './PendingMapView'; -import type {ComponentProps} from './types'; -const MapView = forwardRef((props, ref) => { +const MapView = forwardRef((props, ref) => { const {isOffline} = useNetwork(); const {translate} = useLocalize(); const styles = useThemeStyles(); diff --git a/src/components/MapView/MapViewImpl.website.tsx b/src/components/MapView/MapViewImpl.website.tsx index 5958b2939396..5f68b041602e 100644 --- a/src/components/MapView/MapViewImpl.website.tsx +++ b/src/components/MapView/MapViewImpl.website.tsx @@ -26,13 +26,12 @@ import getCurrentPosition from '@src/libs/getCurrentPosition'; import ONYXKEYS from '@src/ONYXKEYS'; import Direction from './Direction'; import './mapbox.css'; -import type {MapViewHandle} from './MapViewTypes'; +import type {MapViewHandle, MapViewProps} from './MapViewTypes'; import PendingMapView from './PendingMapView'; import responder from './responder'; -import type {ComponentProps} from './types'; import utils from './utils'; -const MapViewImpl = forwardRef( +const MapViewImpl = forwardRef( ( { style, From 3cbc0df8fb4018812d46e92fd52c81b33f86a4ab Mon Sep 17 00:00:00 2001 From: Shahidullah Muffakir Date: Tue, 8 Oct 2024 23:25:09 +0530 Subject: [PATCH 020/438] deleted extra types file from Mapview --- src/components/MapView/MapView.tsx | 5 ++--- src/components/MapView/types.ts | 7 ------- 2 files changed, 2 insertions(+), 10 deletions(-) delete mode 100644 src/components/MapView/types.ts diff --git a/src/components/MapView/MapView.tsx b/src/components/MapView/MapView.tsx index a72788fcc62f..a611c3d62727 100644 --- a/src/components/MapView/MapView.tsx +++ b/src/components/MapView/MapView.tsx @@ -18,13 +18,12 @@ import useLocalize from '@src/hooks/useLocalize'; import useNetwork from '@src/hooks/useNetwork'; import ONYXKEYS from '@src/ONYXKEYS'; import Direction from './Direction'; -import type {MapViewHandle} from './MapViewTypes'; +import type {MapViewHandle, MapViewProps} from './MapViewTypes'; import PendingMapView from './PendingMapView'; import responder from './responder'; -import type {ComponentProps} from './types'; import utils from './utils'; -const MapView = forwardRef( +const MapView = forwardRef( ({accessToken, style, mapPadding, styleURL, pitchEnabled, initialState, waypoints, directionCoordinates, onMapReady, interactive = true}, ref) => { const [userLocation] = useOnyx(ONYXKEYS.USER_LOCATION); const navigation = useNavigation(); diff --git a/src/components/MapView/types.ts b/src/components/MapView/types.ts deleted file mode 100644 index 77b1b9eb82c2..000000000000 --- a/src/components/MapView/types.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type {OnyxEntry} from 'react-native-onyx'; -import type * as OnyxTypes from '@src/types/onyx'; -import type {MapViewProps} from './MapViewTypes'; - -type ComponentProps = MapViewProps; - -export type {ComponentProps}; From 801c3678937740e61db937a5cc32ae33d236fb79 Mon Sep 17 00:00:00 2001 From: Shahidullah Muffakir Date: Tue, 8 Oct 2024 23:39:53 +0530 Subject: [PATCH 021/438] Remove unused @ts-expect-error directive from MapView --- src/components/MapView/MapView.website.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/MapView/MapView.website.tsx b/src/components/MapView/MapView.website.tsx index 52cdaee902a3..b89bfa19e98e 100644 --- a/src/components/MapView/MapView.website.tsx +++ b/src/components/MapView/MapView.website.tsx @@ -50,7 +50,6 @@ const MapView = forwardRef((props, ref) => { } > Date: Mon, 14 Oct 2024 16:36:45 +0700 Subject: [PATCH 022/438] build optimistic change field action --- src/libs/ReportUtils.ts | 58 +++++++++++++++++++++++++++++++++++++- src/libs/actions/Report.ts | 27 ++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 2623fab86a05..a53c3d32d4a1 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -53,7 +53,7 @@ import type {OriginalMessageChangeLog, PaymentMethodType} from '@src/types/onyx/ import type {Status} from '@src/types/onyx/PersonalDetails'; import type {ConnectionName} from '@src/types/onyx/Policy'; import type {NotificationPreference, Participants, PendingChatMember, Participant as ReportParticipant} from '@src/types/onyx/Report'; -import type {Message, ReportActions} from '@src/types/onyx/ReportAction'; +import type {Message, OldDotReportAction, ReportActions} from '@src/types/onyx/ReportAction'; import type {Comment, TransactionChanges, WaypointCollection} from '@src/types/onyx/Transaction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type IconAsset from '@src/types/utils/IconAsset'; @@ -258,6 +258,11 @@ type OptimisticCancelPaymentReportAction = Pick< 'actionName' | 'actorAccountID' | 'message' | 'originalMessage' | 'person' | 'reportActionID' | 'shouldShow' | 'created' | 'pendingAction' >; +type OptimisticChangeFieldAction = Pick< + OldDotReportAction & ReportAction, + 'actionName' | 'actorAccountID' | 'originalMessage' | 'person' | 'reportActionID' | 'created' | 'pendingAction' | 'message' +>; + type OptimisticEditedTaskReportAction = Pick< ReportAction, 'reportActionID' | 'actionName' | 'pendingAction' | 'actorAccountID' | 'automatic' | 'avatar' | 'created' | 'shouldShow' | 'message' | 'person' | 'delegateAccountID' @@ -2539,6 +2544,56 @@ function getReimbursementDeQueuedActionMessage( return Localize.translateLocal('iou.canceledRequest', {submitterDisplayName, amount: formattedAmount}); } +/** + * Builds an optimistic REIMBURSEMENT_DEQUEUED report action with a randomly generated reportActionID. + * + */ +function buildOptimisticChangeFieldAction(reportField: PolicyReportField, previousReportField: PolicyReportField): OptimisticChangeFieldAction { + return { + actionName: CONST.REPORT.ACTIONS.TYPE.CHANGE_FIELD, + actorAccountID: currentUserAccountID, + message: [ + { + type: 'TEXT', + style: 'strong', + text: 'You', + }, + { + type: 'TEXT', + style: 'normal', + text: ` modified field '${reportField.name}'.`, + }, + { + type: 'TEXT', + style: 'normal', + text: ` New value is '${reportField.value}'`, + }, + { + type: 'TEXT', + style: 'normal', + text: ` (previously '${previousReportField.value}').`, + }, + ], + originalMessage: { + fieldName: reportField.name, + newType: reportField.type, + newValue: reportField.value, + oldType: previousReportField.type, + oldValue: previousReportField.value, + }, + person: [ + { + style: 'strong', + text: getCurrentUserDisplayNameOrEmail(), + type: 'TEXT', + }, + ], + reportActionID: NumberUtils.rand64(), + created: DateUtils.getDBTime(), + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }; +} + /** * Builds an optimistic REIMBURSEMENT_DEQUEUED report action with a randomly generated reportActionID. * @@ -8470,6 +8525,7 @@ export { hasMissingInvoiceBankAccount, reasonForReportToBeInOptionList, getReasonAndReportActionThatRequiresAttention, + buildOptimisticChangeFieldAction, }; export type { diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 13b14d380758..57d5d7e8e287 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1909,6 +1909,8 @@ function updateReportField(reportID: string, reportField: PolicyReportField, pre const fieldViolation = ReportUtils.getFieldViolation(reportViolations, reportField); const recentlyUsedValues = allRecentlyUsedReportFields?.[fieldKey] ?? []; + const optimisticChangeFieldAction = ReportUtils.buildOptimisticChangeFieldAction(reportField, previousReportField); + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, @@ -1922,6 +1924,13 @@ function updateReportField(reportID: string, reportField: PolicyReportField, pre }, }, }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, + value: { + [optimisticChangeFieldAction.reportActionID]: optimisticChangeFieldAction, + }, + }, ]; if (fieldViolation) { @@ -1962,6 +1971,15 @@ function updateReportField(reportID: string, reportField: PolicyReportField, pre }, }, }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, + value: { + [optimisticChangeFieldAction.reportActionID]: { + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericUpdateReportFieldFailureMessage'), + }, + }, + }, ]; if (reportField.type === 'dropdown') { @@ -1987,6 +2005,15 @@ function updateReportField(reportID: string, reportField: PolicyReportField, pre }, }, }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, + value: { + [optimisticChangeFieldAction.reportActionID]: { + pendingAction: null, + }, + }, + }, ]; const parameters = { From 3fb2bea0fc36361b555dfbe410c4dc26f8f9c8a9 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 14 Oct 2024 21:42:23 +0700 Subject: [PATCH 023/438] add parameter --- src/libs/actions/Report.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 57d5d7e8e287..f69c3fce4fb5 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2019,6 +2019,7 @@ function updateReportField(reportID: string, reportField: PolicyReportField, pre const parameters = { reportID, reportFields: JSON.stringify({[fieldKey]: reportField}), + reportFieldsActionIDs: JSON.stringify({[fieldKey]: optimisticChangeFieldAction.reportActionID}), }; API.write(WRITE_COMMANDS.SET_REPORT_FIELD, parameters, {optimisticData, failureData, successData}); From 55d5d6905246c7d0120e813ce8bb8eb0a7e1c91a Mon Sep 17 00:00:00 2001 From: mkzie2 Date: Tue, 15 Oct 2024 09:16:16 +0700 Subject: [PATCH 024/438] fix: incorrect lhn message when partially pay held report --- src/libs/ReportUtils.ts | 12 +++++++++++- src/libs/actions/IOU.ts | 28 +++++++++++++++++++--------- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 2623fab86a05..08962d8597c4 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -446,6 +446,7 @@ type OptimisticIOUReport = Pick< | 'parentReportID' | 'lastVisibleActionCreated' | 'fieldList' + | 'parentReportActionID' >; type DisplayNameWithTooltips = Array>; @@ -4410,7 +4411,15 @@ function buildOptimisticTaskCommentReportAction( * @param isSendingMoney - If we pay someone the IOU should be created as settled */ -function buildOptimisticIOUReport(payeeAccountID: number, payerAccountID: number, total: number, chatReportID: string, currency: string, isSendingMoney = false): OptimisticIOUReport { +function buildOptimisticIOUReport( + payeeAccountID: number, + payerAccountID: number, + total: number, + chatReportID: string, + currency: string, + isSendingMoney = false, + parentReportActionID?: string, +): OptimisticIOUReport { const formattedTotal = CurrencyUtils.convertToDisplayString(total, currency); const personalDetails = getPersonalDetailsForAccountID(payerAccountID); const payerEmail = 'login' in personalDetails ? personalDetails.login : ''; @@ -4440,6 +4449,7 @@ function buildOptimisticIOUReport(payeeAccountID: number, payerAccountID: number parentReportID: chatReportID, lastVisibleActionCreated: DateUtils.getDBTime(), fieldList: policy?.fieldList, + parentReportActionID, }; } diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index de896e6f72f5..dc0bf138b43e 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -6472,15 +6472,25 @@ function getReportFromHoldRequestsOnyxData( const firstHoldTransaction = holdTransactions.at(0); const newParentReportActionID = rand64(); - const optimisticExpenseReport = ReportUtils.buildOptimisticExpenseReport( - chatReport.reportID, - chatReport.policyID ?? iouReport?.policyID ?? '', - recipient.accountID ?? 1, - holdTransactions.reduce((acc, transaction) => acc + transaction.amount, 0) * (ReportUtils.isIOUReport(iouReport) ? 1 : -1), - getCurrency(firstHoldTransaction), - false, - newParentReportActionID, - ); + const optimisticExpenseReport = ReportUtils.isExpenseReport(iouReport) + ? ReportUtils.buildOptimisticExpenseReport( + chatReport.reportID, + chatReport.policyID ?? iouReport?.policyID ?? '', + recipient.accountID ?? 1, + holdTransactions.reduce((acc, transaction) => acc + transaction.amount, 0) * (ReportUtils.isIOUReport(iouReport) ? 1 : -1), + getCurrency(firstHoldTransaction), + false, + newParentReportActionID, + ) + : ReportUtils.buildOptimisticIOUReport( + iouReport?.ownerAccountID ?? -1, + iouReport?.managerID ?? -1, + holdTransactions.reduce((acc, transaction) => acc + transaction.amount, 0) * (ReportUtils.isIOUReport(iouReport) ? 1 : -1), + chatReport.reportID, + getCurrency(firstHoldTransaction), + false, + newParentReportActionID, + ); const optimisticExpenseReportPreview = ReportUtils.buildOptimisticReportPreview( chatReport, optimisticExpenseReport, From 58663255a5c7d8eaf92eb41dd27b93998426bda7 Mon Sep 17 00:00:00 2001 From: Anusha Date: Wed, 16 Oct 2024 23:49:06 +0500 Subject: [PATCH 025/438] hide expensify from new chat page --- src/libs/OptionsListUtils.ts | 2 +- src/pages/NewChatPage.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index fbf2f3b94c7c..1ede98338f87 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -1994,7 +1994,7 @@ function getOptions( allPersonalDetailsOptions = lodashOrderBy(allPersonalDetailsOptions, [(personalDetail) => personalDetail.text?.toLowerCase()], 'asc'); } - const optionsToExclude: Option[] = []; + const optionsToExclude: Option[] = [{login: CONST.EMAIL.NOTIFICATIONS}]; // If we're including selected options from the search results, we only want to exclude them if the search input is empty // This is because on certain pages, we show the selected options at the top when the search input is empty diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx index c406f7f3058c..494e099933fe 100755 --- a/src/pages/NewChatPage.tsx +++ b/src/pages/NewChatPage.tsx @@ -229,7 +229,7 @@ function NewChatPage({isGroupChat}: NewChatPageProps) { const itemRightSideComponent = useCallback( (item: ListItem & OptionsListUtils.Option, isFocused?: boolean) => { - if (!!item.isSelfDM || (item.accountID && CONST.NON_ADDABLE_ACCOUNT_IDS.includes(item.accountID))) { + if (!!item.isSelfDM || (item.login && excludedGroupEmails.includes(item.login))) { return null; } /** From 039f0d6982b91199c18400bf4bafccfa3a0a6390 Mon Sep 17 00:00:00 2001 From: Anusha Date: Thu, 17 Oct 2024 00:12:55 +0500 Subject: [PATCH 026/438] fix type error --- src/pages/NewChatPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx index 494e099933fe..bd3e30a48cf4 100755 --- a/src/pages/NewChatPage.tsx +++ b/src/pages/NewChatPage.tsx @@ -38,7 +38,7 @@ type NewChatPageProps = { isGroupChat?: boolean; }; -const excludedGroupEmails = CONST.EXPENSIFY_EMAILS.filter((value) => value !== CONST.EMAIL.CONCIERGE); +const excludedGroupEmails: Array = CONST.EXPENSIFY_EMAILS.filter((value) => value !== CONST.EMAIL.CONCIERGE); function useOptions({isGroupChat}: NewChatPageProps) { const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState(''); From 0fd9a41a1e274fbf11b4d7c0b8aef6f9ade3d4aa Mon Sep 17 00:00:00 2001 From: Anusha Date: Thu, 17 Oct 2024 00:28:04 +0500 Subject: [PATCH 027/438] fix lint error --- src/pages/NewChatPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx index bd3e30a48cf4..5b44659babab 100755 --- a/src/pages/NewChatPage.tsx +++ b/src/pages/NewChatPage.tsx @@ -38,7 +38,7 @@ type NewChatPageProps = { isGroupChat?: boolean; }; -const excludedGroupEmails: Array = CONST.EXPENSIFY_EMAILS.filter((value) => value !== CONST.EMAIL.CONCIERGE); +const excludedGroupEmails: string[] = CONST.EXPENSIFY_EMAILS.filter((value) => value !== CONST.EMAIL.CONCIERGE); function useOptions({isGroupChat}: NewChatPageProps) { const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState(''); From d7465bf302dc4c1cfed47540c500af19beed8f9e Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Thu, 17 Oct 2024 17:15:45 +0200 Subject: [PATCH 028/438] simplify offline message check --- src/hooks/useNetwork.ts | 2 +- src/pages/home/report/ReportActionsList.tsx | 23 +++++++++++---------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/hooks/useNetwork.ts b/src/hooks/useNetwork.ts index 5a686b5ab138..21372daf424e 100644 --- a/src/hooks/useNetwork.ts +++ b/src/hooks/useNetwork.ts @@ -59,5 +59,5 @@ export default function useNetwork({onReconnect = () => {}}: UseNetworkProps = { // eslint-disable-next-line react-hooks/exhaustive-deps }, [isOffline]); - return {isOffline, lastOfflineAt, lastOnlineAt}; + return useMemo(() => ({isOffline, lastOfflineAt, lastOnlineAt}), [isOffline, lastOfflineAt, lastOnlineAt]); } diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index 1527fb90b459..caa289f87f27 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -229,7 +229,9 @@ function ReportActionsList({ const isMessageOffline = useCallback( (m: OnyxTypes.ReportAction) => wasMessageReceivedWhileOffline(m, lastOfflineAt, lastOnlineAt, preferredLocale), - [lastOfflineAt, lastOnlineAt, preferredLocale], + // eslint-disable-next-line react-compiler/react-compiler + // eslint-disable-next-line react-hooks/exhaustive-deps + [lastOfflineAt, lastOnlineAt], ); /** @@ -239,18 +241,17 @@ function ReportActionsList({ const shouldDisplayNewMarker = (message: OnyxTypes.ReportAction, index: number): boolean => { const nextMessage = sortedVisibleReportActions.at(index + 1); + const isCurrentMessageUnread = isMessageUnread(message, unreadMarkerTime); + const isNextMessageUnread = nextMessage ? isMessageUnread(nextMessage, unreadMarkerTime) : false; + // If the user recevied new messages while being offline, we want to display the unread marker above the first offline message. - if (!ReportActionsUtils.wasActionTakenByCurrentUser(message)) { - const isCurrentMessageOffline = isMessageOffline(message); - const isNextMessageOffline = nextMessage && !ReportActionsUtils.wasActionTakenByCurrentUser(nextMessage) && isMessageOffline(nextMessage); - if (isCurrentMessageOffline && !isNextMessageOffline) { - return true; - } - } + const wasCurrentMessageReceivedWhileOffline = !ReportActionsUtils.wasActionTakenByCurrentUser(message) && isMessageOffline(message); + const wasNextMessageReceivedWhileOffline = nextMessage && !ReportActionsUtils.wasActionTakenByCurrentUser(nextMessage) && isMessageOffline(nextMessage); + + const shouldDisplayForCurrentMessage = isCurrentMessageUnread || wasCurrentMessageReceivedWhileOffline; + const shouldDisplayForNextMessage = isNextMessageUnread || wasNextMessageReceivedWhileOffline; + const shouldDisplay = shouldDisplayForCurrentMessage && !shouldDisplayForNextMessage && !ReportActionsUtils.shouldHideNewMarker(message); - const isCurrentMessageUnread = isMessageUnread(message, unreadMarkerTime); - const isNextMessageRead = !nextMessage || !isMessageUnread(nextMessage, unreadMarkerTime); - const shouldDisplay = isCurrentMessageUnread && isNextMessageRead && !ReportActionsUtils.shouldHideNewMarker(message); const isWithinVisibleThreshold = scrollingVerticalOffset.current < MSG_VISIBLE_THRESHOLD ? message.created < (userActiveSince.current ?? '') : true; return shouldDisplay && isWithinVisibleThreshold; }; From dd99fe7e1f229d8b73f31fab2c93137e62595647 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Thu, 17 Oct 2024 17:32:33 +0200 Subject: [PATCH 029/438] simplify code --- src/pages/home/report/ReportActionsList.tsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index caa289f87f27..3f2133044977 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -122,7 +122,7 @@ function keyExtractor(item: OnyxTypes.ReportAction): string { return item.reportActionID; } -function wasMessageReceivedWhileOffline(message: OnyxTypes.ReportAction, offlineLastAt: Date | undefined, onlineLastAt: Date | undefined, locale: OnyxTypes.Locale): boolean { +function wasMessageCreatedWhileOffline(message: OnyxTypes.ReportAction, offlineLastAt: Date | undefined, onlineLastAt: Date | undefined, locale: OnyxTypes.Locale): boolean { if (!onlineLastAt || !offlineLastAt) { return false; } @@ -227,8 +227,8 @@ function ReportActionsList({ // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, [report.reportID]); - const isMessageOffline = useCallback( - (m: OnyxTypes.ReportAction) => wasMessageReceivedWhileOffline(m, lastOfflineAt, lastOnlineAt, preferredLocale), + const wasMessageReceivedWhileOffline = useCallback( + (m: OnyxTypes.ReportAction) => !ReportActionsUtils.wasActionTakenByCurrentUser && wasMessageCreatedWhileOffline(m, lastOfflineAt, lastOnlineAt, preferredLocale), // eslint-disable-next-line react-compiler/react-compiler // eslint-disable-next-line react-hooks/exhaustive-deps [lastOfflineAt, lastOnlineAt], @@ -245,11 +245,14 @@ function ReportActionsList({ const isNextMessageUnread = nextMessage ? isMessageUnread(nextMessage, unreadMarkerTime) : false; // If the user recevied new messages while being offline, we want to display the unread marker above the first offline message. - const wasCurrentMessageReceivedWhileOffline = !ReportActionsUtils.wasActionTakenByCurrentUser(message) && isMessageOffline(message); - const wasNextMessageReceivedWhileOffline = nextMessage && !ReportActionsUtils.wasActionTakenByCurrentUser(nextMessage) && isMessageOffline(nextMessage); + const wasCurrentMessageReceivedWhileOffline = wasMessageReceivedWhileOffline(message); + const wasNextMessageReceivedWhileOffline = nextMessage && wasMessageReceivedWhileOffline(nextMessage); const shouldDisplayForCurrentMessage = isCurrentMessageUnread || wasCurrentMessageReceivedWhileOffline; const shouldDisplayForNextMessage = isNextMessageUnread || wasNextMessageReceivedWhileOffline; + + console.log({wasCurrentMessageReceivedWhileOffline, wasNextMessageReceivedWhileOffline, shouldDisplayForCurrentMessage, shouldDisplayForNextMessage}); + const shouldDisplay = shouldDisplayForCurrentMessage && !shouldDisplayForNextMessage && !ReportActionsUtils.shouldHideNewMarker(message); const isWithinVisibleThreshold = scrollingVerticalOffset.current < MSG_VISIBLE_THRESHOLD ? message.created < (userActiveSince.current ?? '') : true; @@ -266,7 +269,7 @@ function ReportActionsList({ } return null; - }, [sortedVisibleReportActions, unreadMarkerTime, isMessageOffline]); + }, [sortedVisibleReportActions, unreadMarkerTime, wasMessageReceivedWhileOffline]); /** * Subscribe to read/unread events and update our unreadMarkerTime From 0678a2524715927bffeae046fabd3f5a8202a601 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Thu, 17 Oct 2024 23:00:30 +0200 Subject: [PATCH 030/438] WIP: improve unread marker check --- src/hooks/useNetwork.ts | 2 +- src/pages/home/report/ReportActionsList.tsx | 23 +++++++++------------ 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/hooks/useNetwork.ts b/src/hooks/useNetwork.ts index 21372daf424e..5a686b5ab138 100644 --- a/src/hooks/useNetwork.ts +++ b/src/hooks/useNetwork.ts @@ -59,5 +59,5 @@ export default function useNetwork({onReconnect = () => {}}: UseNetworkProps = { // eslint-disable-next-line react-hooks/exhaustive-deps }, [isOffline]); - return useMemo(() => ({isOffline, lastOfflineAt, lastOnlineAt}), [isOffline, lastOfflineAt, lastOnlineAt]); + return {isOffline, lastOfflineAt, lastOnlineAt}; } diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index 3f2133044977..b3ae47214362 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -229,9 +229,7 @@ function ReportActionsList({ const wasMessageReceivedWhileOffline = useCallback( (m: OnyxTypes.ReportAction) => !ReportActionsUtils.wasActionTakenByCurrentUser && wasMessageCreatedWhileOffline(m, lastOfflineAt, lastOnlineAt, preferredLocale), - // eslint-disable-next-line react-compiler/react-compiler - // eslint-disable-next-line react-hooks/exhaustive-deps - [lastOfflineAt, lastOnlineAt], + [lastOfflineAt, lastOnlineAt, preferredLocale], ); /** @@ -239,24 +237,23 @@ function ReportActionsList({ */ const unreadMarkerReportActionID = useMemo(() => { const shouldDisplayNewMarker = (message: OnyxTypes.ReportAction, index: number): boolean => { + const isWithinVisibleThreshold = scrollingVerticalOffset.current < MSG_VISIBLE_THRESHOLD ? message.created < (userActiveSince.current ?? '') : true; + const nextMessage = sortedVisibleReportActions.at(index + 1); const isCurrentMessageUnread = isMessageUnread(message, unreadMarkerTime); const isNextMessageUnread = nextMessage ? isMessageUnread(nextMessage, unreadMarkerTime) : false; // If the user recevied new messages while being offline, we want to display the unread marker above the first offline message. - const wasCurrentMessageReceivedWhileOffline = wasMessageReceivedWhileOffline(message); - const wasNextMessageReceivedWhileOffline = nextMessage && wasMessageReceivedWhileOffline(nextMessage); - - const shouldDisplayForCurrentMessage = isCurrentMessageUnread || wasCurrentMessageReceivedWhileOffline; - const shouldDisplayForNextMessage = isNextMessageUnread || wasNextMessageReceivedWhileOffline; + const isCurrentMessageOffline = wasMessageReceivedWhileOffline(message); + const isNextMessageOffline = nextMessage ? wasMessageReceivedWhileOffline(nextMessage) : false; - console.log({wasCurrentMessageReceivedWhileOffline, wasNextMessageReceivedWhileOffline, shouldDisplayForCurrentMessage, shouldDisplayForNextMessage}); + const shouldDisplayForNextMessage = isNextMessageUnread || isNextMessageOffline; + const shouldDisplayBecauseUnread = isCurrentMessageUnread && !isNextMessageUnread && !ReportActionsUtils.shouldHideNewMarker(message) && isWithinVisibleThreshold; + const shouldDisplayBecauseOffline = isCurrentMessageOffline && !isNextMessageOffline; + const shouldDisplay = shouldDisplayBecauseOffline || shouldDisplayBecauseUnread; - const shouldDisplay = shouldDisplayForCurrentMessage && !shouldDisplayForNextMessage && !ReportActionsUtils.shouldHideNewMarker(message); - - const isWithinVisibleThreshold = scrollingVerticalOffset.current < MSG_VISIBLE_THRESHOLD ? message.created < (userActiveSince.current ?? '') : true; - return shouldDisplay && isWithinVisibleThreshold; + return shouldDisplay; }; // Scan through each visible report action until we find the appropriate action to show the unread marker From 1900337a0ca1aeb7573b3eb7cf961d17c6b91842 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Thu, 17 Oct 2024 23:16:22 +0200 Subject: [PATCH 031/438] fix: unread marker not shown --- src/hooks/useNetwork.ts | 2 +- src/pages/home/report/ReportActionsList.tsx | 21 +++++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/hooks/useNetwork.ts b/src/hooks/useNetwork.ts index 5a686b5ab138..21372daf424e 100644 --- a/src/hooks/useNetwork.ts +++ b/src/hooks/useNetwork.ts @@ -59,5 +59,5 @@ export default function useNetwork({onReconnect = () => {}}: UseNetworkProps = { // eslint-disable-next-line react-hooks/exhaustive-deps }, [isOffline]); - return {isOffline, lastOfflineAt, lastOnlineAt}; + return useMemo(() => ({isOffline, lastOfflineAt, lastOnlineAt}), [isOffline, lastOfflineAt, lastOnlineAt]); } diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index b3ae47214362..3326dd49dce7 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -228,8 +228,10 @@ function ReportActionsList({ }, [report.reportID]); const wasMessageReceivedWhileOffline = useCallback( - (m: OnyxTypes.ReportAction) => !ReportActionsUtils.wasActionTakenByCurrentUser && wasMessageCreatedWhileOffline(m, lastOfflineAt, lastOnlineAt, preferredLocale), - [lastOfflineAt, lastOnlineAt, preferredLocale], + (message: OnyxTypes.ReportAction) => !ReportActionsUtils.wasActionTakenByCurrentUser(message) && wasMessageCreatedWhileOffline(message, lastOfflineAt, lastOnlineAt, preferredLocale), + // eslint-disable-next-line react-compiler/react-compiler + // eslint-disable-next-line react-hooks/exhaustive-deps + [lastOfflineAt, lastOnlineAt], ); /** @@ -241,19 +243,18 @@ function ReportActionsList({ const nextMessage = sortedVisibleReportActions.at(index + 1); - const isCurrentMessageUnread = isMessageUnread(message, unreadMarkerTime); - const isNextMessageUnread = nextMessage ? isMessageUnread(nextMessage, unreadMarkerTime) : false; - // If the user recevied new messages while being offline, we want to display the unread marker above the first offline message. const isCurrentMessageOffline = wasMessageReceivedWhileOffline(message); - const isNextMessageOffline = nextMessage ? wasMessageReceivedWhileOffline(nextMessage) : false; + const isNextMessageOffline = !!nextMessage && wasMessageReceivedWhileOffline(nextMessage); + + const isCurrentMessageUnread = isMessageUnread(message, unreadMarkerTime); + const isNextMessageUnread = !!nextMessage && isMessageUnread(nextMessage, unreadMarkerTime); const shouldDisplayForNextMessage = isNextMessageUnread || isNextMessageOffline; - const shouldDisplayBecauseUnread = isCurrentMessageUnread && !isNextMessageUnread && !ReportActionsUtils.shouldHideNewMarker(message) && isWithinVisibleThreshold; - const shouldDisplayBecauseOffline = isCurrentMessageOffline && !isNextMessageOffline; - const shouldDisplay = shouldDisplayBecauseOffline || shouldDisplayBecauseUnread; + const shouldDisplayBecauseOffline = isCurrentMessageOffline && !shouldDisplayForNextMessage; + const shouldDisplayBecauseUnread = isCurrentMessageUnread && !shouldDisplayForNextMessage && !ReportActionsUtils.shouldHideNewMarker(message) && isWithinVisibleThreshold; - return shouldDisplay; + return shouldDisplayBecauseOffline || shouldDisplayBecauseUnread; }; // Scan through each visible report action until we find the appropriate action to show the unread marker From 8ccedd770cd46b5d09263feff1d466091fd2d5a7 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Fri, 18 Oct 2024 13:57:43 +0700 Subject: [PATCH 032/438] Add policyID param to CompleteOnboarding API --- src/libs/API/parameters/CompleteGuidedSetupParams.ts | 1 + src/libs/actions/Report.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/libs/API/parameters/CompleteGuidedSetupParams.ts b/src/libs/API/parameters/CompleteGuidedSetupParams.ts index 1242b9285de9..6ff45ecc424a 100644 --- a/src/libs/API/parameters/CompleteGuidedSetupParams.ts +++ b/src/libs/API/parameters/CompleteGuidedSetupParams.ts @@ -9,6 +9,7 @@ type CompleteGuidedSetupParams = { paymentSelected?: string; companySize?: OnboardingCompanySizeType; userReportedIntegration?: OnboardingAccountingType; + policyID?: string; }; export default CompleteGuidedSetupParams; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 7071c96f8612..acd22cacaa2b 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3715,6 +3715,7 @@ function completeOnboarding( paymentSelected, companySize, userReportedIntegration, + policyID: onboardingPolicyID, }; API.write(WRITE_COMMANDS.COMPLETE_GUIDED_SETUP, parameters, {optimisticData, successData, failureData}); From 3620918bf11759e18a7d23ae49faa4eacf1a211f Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Sun, 20 Oct 2024 19:16:12 +0530 Subject: [PATCH 033/438] add selftourviewed command --- src/libs/API/types.ts | 2 ++ src/libs/actions/Welcome/index.ts | 17 ++++++++++++++++- src/types/onyx/Onboarding.ts | 3 +++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index 929e709559b7..0bfd77c4dcd0 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -429,6 +429,7 @@ const WRITE_COMMANDS = { SET_CARD_EXPORT_ACCOUNT: 'SetCardExportAccount', SET_MISSING_PERSONAL_DETAILS_AND_SHIP_EXPENSIFY_CARD: 'SetMissingPersonalDetailsAndShipExpensifyCard', SET_INVOICING_TRANSFER_BANK_ACCOUNT: 'SetInvoicingTransferBankAccount', + SELF_TOUR_VIEWED: 'SelfTourViewed', } as const; type WriteCommand = ValueOf; @@ -868,6 +869,7 @@ type WriteCommandParameters = { [WRITE_COMMANDS.UPDATE_XERO_SYNC_REIMBURSEMENT_ACCOUNT_ID]: Parameters.UpdateXeroGenericTypeParams; [WRITE_COMMANDS.SET_INVOICING_TRANSFER_BANK_ACCOUNT]: Parameters.SetInvoicingTransferBankAccountParams; + [WRITE_COMMANDS.SELF_TOUR_VIEWED]: null; }; const READ_COMMANDS = { diff --git a/src/libs/actions/Welcome/index.ts b/src/libs/actions/Welcome/index.ts index d504c5550331..19a570ab610f 100644 --- a/src/libs/actions/Welcome/index.ts +++ b/src/libs/actions/Welcome/index.ts @@ -2,7 +2,7 @@ import {NativeModules} from 'react-native'; import type {OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import * as API from '@libs/API'; -import {SIDE_EFFECT_REQUEST_COMMANDS} from '@libs/API/types'; +import {SIDE_EFFECT_REQUEST_COMMANDS, WRITE_COMMANDS} from '@libs/API/types'; import Log from '@libs/Log'; import type {OnboardingCompanySizeType, OnboardingPurposeType} from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -183,6 +183,20 @@ function resetAllChecks() { OnboardingFlow.clearInitialPath(); } +function setSelfTourViewed() { + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.NVP_ONBOARDING, + value: { + selfTourViewed: true, + }, + }, + ]; + + API.write(WRITE_COMMANDS.SELF_TOUR_VIEWED, null, {optimisticData}); +} + export { onServerDataReady, isOnboardingFlowCompleted, @@ -195,4 +209,5 @@ export { completeHybridAppOnboarding, setOnboardingErrorMessage, setOnboardingCompanySize, + setSelfTourViewed, }; diff --git a/src/types/onyx/Onboarding.ts b/src/types/onyx/Onboarding.ts index 4b6a52f25cb4..2cf8eccba1c2 100644 --- a/src/types/onyx/Onboarding.ts +++ b/src/types/onyx/Onboarding.ts @@ -11,6 +11,9 @@ type Onboarding = { /** A string that informs which qualifier the user selected during sign up */ signupQualifier: ValueOf; + + /** A Boolean that tells whether the user has seen navattic tour */ + selfTourViewed?: boolean; }; export default Onboarding; From 31ff69bb79ad057aafd7825c5598bb156c7f49b0 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Sun, 20 Oct 2024 20:00:06 +0530 Subject: [PATCH 034/438] add util to get tour url --- src/CONST.ts | 6 ++++-- src/libs/TourUtils.ts | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 src/libs/TourUtils.ts diff --git a/src/CONST.ts b/src/CONST.ts index 84003710938a..ff9f960c756e 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -748,8 +748,10 @@ const CONST = { // Use Environment.getEnvironmentURL to get the complete URL with port number DEV_NEW_EXPENSIFY_URL: 'https://dev.new.expensify.com:', NAVATTIC: { - ADMIN_TOUR: 'https://expensify.navattic.com/kh204a7', - EMPLOYEE_TOUR: 'https://expensify.navattic.com/35609gb', + ADMIN_TOUR_PRODUCTION: 'https://expensify.navattic.com/kh204a7', + ADMIN_TOUR_STAGING: 'https://expensify.navattic.com/stagingAdmin', + EMPLOYEE_TOUR_PRODUCTION: 'https://expensify.navattic.com/35609gb', + EMPLOYEE_TOUR_STAGING: 'https://expensify.navattic.com/stagingEmployee', }, OLDDOT_URLS: { diff --git a/src/libs/TourUtils.ts b/src/libs/TourUtils.ts new file mode 100644 index 000000000000..a88ee47cc563 --- /dev/null +++ b/src/libs/TourUtils.ts @@ -0,0 +1,14 @@ +import type {ValueOf} from 'type-fest'; +import CONST from '@src/CONST'; +import type {OnboardingPurposeType} from '@src/CONST'; + +function getNavatticURL(environment: ValueOf, introSelected?: OnboardingPurposeType) { + const adminTourURL = environment === CONST.ENVIRONMENT.PRODUCTION ? CONST.NAVATTIC.ADMIN_TOUR_PRODUCTION : CONST.NAVATTIC.ADMIN_TOUR_STAGING; + const employeeTourURL = environment === CONST.ENVIRONMENT.PRODUCTION ? CONST.NAVATTIC.EMPLOYEE_TOUR_PRODUCTION : CONST.NAVATTIC.EMPLOYEE_TOUR_STAGING; + return introSelected === CONST.SELECTABLE_ONBOARDING_CHOICES.MANAGE_TEAM ? adminTourURL : employeeTourURL; +} + +export { + // eslint-disable-next-line import/prefer-default-export + getNavatticURL, +}; From 528395398d39a00243c860a4d1553559de8a5c54 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Sun, 20 Oct 2024 20:02:57 +0530 Subject: [PATCH 035/438] use util to get url --- src/pages/Search/EmptySearchView.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/pages/Search/EmptySearchView.tsx b/src/pages/Search/EmptySearchView.tsx index c148f1b56d62..8bcb3ee7b46d 100644 --- a/src/pages/Search/EmptySearchView.tsx +++ b/src/pages/Search/EmptySearchView.tsx @@ -10,12 +10,14 @@ import MenuItem from '@components/MenuItem'; import SearchRowSkeleton from '@components/Skeletons/SearchRowSkeleton'; import Text from '@components/Text'; import TextLink from '@components/TextLink'; +import useEnvironment from '@hooks/useEnvironment'; import useLocalize from '@hooks/useLocalize'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import interceptAnonymousUser from '@libs/interceptAnonymousUser'; import * as ReportUtils from '@libs/ReportUtils'; +import {getNavatticURL} from '@libs/TourUtils'; import * as TripsResevationUtils from '@libs/TripReservationUtils'; import variables from '@styles/variables'; import * as IOU from '@userActions/IOU'; @@ -93,7 +95,8 @@ function EmptySearchView({type}: EmptySearchViewProps) { }, [styles, translate, ctaErrorMessage]); const [onboardingPurpose] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED, {selector: (introSelected) => introSelected?.choice}); - const navatticLink = onboardingPurpose === CONST.SELECTABLE_ONBOARDING_CHOICES.MANAGE_TEAM ? CONST.NAVATTIC.ADMIN_TOUR : CONST.NAVATTIC.EMPLOYEE_TOUR; + const {environment} = useEnvironment(); + const navatticURL = getNavatticURL(environment, onboardingPurpose); const content = useMemo(() => { switch (type) { @@ -120,7 +123,7 @@ function EmptySearchView({type}: EmptySearchViewProps) { title: translate('search.searchResults.emptyExpenseResults.title'), subtitle: translate('search.searchResults.emptyExpenseResults.subtitle'), buttons: [ - {buttonText: translate('emptySearchView.takeATour'), buttonAction: () => Link.openExternalLink(navatticLink)}, + {buttonText: translate('emptySearchView.takeATour'), buttonAction: () => Link.openExternalLink(navatticURL)}, { buttonText: translate('iou.createExpense'), buttonAction: () => interceptAnonymousUser(() => IOU.startMoneyRequest(CONST.IOU.TYPE.CREATE, ReportUtils.generateReportID())), @@ -140,7 +143,7 @@ function EmptySearchView({type}: EmptySearchViewProps) { headerContentStyles: styles.emptyStateFolderWebStyles, }; } - }, [type, StyleUtils, translate, theme, styles, subtitleComponent, ctaErrorMessage, navatticLink]); + }, [type, StyleUtils, translate, theme, styles, subtitleComponent, ctaErrorMessage, navatticURL]); return ( Date: Sun, 20 Oct 2024 20:27:57 +0530 Subject: [PATCH 036/438] fix staging url --- src/CONST.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index ff9f960c756e..01466d8baf86 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -749,9 +749,9 @@ const CONST = { DEV_NEW_EXPENSIFY_URL: 'https://dev.new.expensify.com:', NAVATTIC: { ADMIN_TOUR_PRODUCTION: 'https://expensify.navattic.com/kh204a7', - ADMIN_TOUR_STAGING: 'https://expensify.navattic.com/stagingAdmin', + ADMIN_TOUR_STAGING: 'https://expensify.navattic.com/3i300k18', EMPLOYEE_TOUR_PRODUCTION: 'https://expensify.navattic.com/35609gb', - EMPLOYEE_TOUR_STAGING: 'https://expensify.navattic.com/stagingEmployee', + EMPLOYEE_TOUR_STAGING: 'https://expensify.navattic.com/cf15002s', }, OLDDOT_URLS: { From c1081d02784a0020a5dde6b865f582afcbd98b84 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Sun, 20 Oct 2024 20:28:05 +0530 Subject: [PATCH 037/438] add tour icon --- assets/images/tour.svg | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 assets/images/tour.svg diff --git a/assets/images/tour.svg b/assets/images/tour.svg new file mode 100644 index 000000000000..926cfcd8de69 --- /dev/null +++ b/assets/images/tour.svg @@ -0,0 +1,6 @@ + + + + + + From cd6558b6a095cd842500ad22f7466e9219acc7f8 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Sun, 20 Oct 2024 20:28:12 +0530 Subject: [PATCH 038/438] add tour icon --- src/components/Icon/Expensicons.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/Icon/Expensicons.ts b/src/components/Icon/Expensicons.ts index cd9c97105ff0..621bc5e43bea 100644 --- a/src/components/Icon/Expensicons.ts +++ b/src/components/Icon/Expensicons.ts @@ -182,6 +182,7 @@ import Task from '@assets/images/task.svg'; import Thread from '@assets/images/thread.svg'; import ThreeDots from '@assets/images/three-dots.svg'; import ThumbsUp from '@assets/images/thumbs-up.svg'; +import Tour from '@assets/images/tour.svg'; import Transfer from '@assets/images/transfer.svg'; import Trashcan from '@assets/images/trashcan.svg'; import Unlock from '@assets/images/unlock.svg'; @@ -404,4 +405,5 @@ export { Bookmark, Star, QBDSquare, + Tour, }; From 38820777379e9e84eb42231e4e88bdbdc1f2364d Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Sun, 20 Oct 2024 20:29:12 +0530 Subject: [PATCH 039/438] add lang --- src/languages/en.ts | 4 ++++ src/languages/es.ts | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/languages/en.ts b/src/languages/en.ts index 5798f7fe48e9..e68e89296222 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -5132,6 +5132,10 @@ const translations = { emptySearchView: { takeATour: 'Take a tour', }, + tour: { + takeATwoMinuteTour: 'Take a 2-minute tour', + exploreExpensify: 'Explore everything Expensify has to offer', + }, }; export default translations satisfies TranslationDeepObject; diff --git a/src/languages/es.ts b/src/languages/es.ts index 84c03d5d9bf3..9a24b1bf9386 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -5649,6 +5649,10 @@ const translations = { emptySearchView: { takeATour: 'Haz un tour', }, + tour: { + takeATwoMinuteTour: 'Haz un tour de 2 minutos', + exploreExpensify: 'Explora todo lo que Expensify tiene para ofrecer', + }, }; export default translations satisfies TranslationDeepObject; From 469c0520a80fd3f6a97961c0b99b86f2591be2ab Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Sun, 20 Oct 2024 20:29:25 +0530 Subject: [PATCH 040/438] add onboarding selftourviewed selector --- src/libs/onboardingSelectors.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/libs/onboardingSelectors.ts b/src/libs/onboardingSelectors.ts index efa67d2aed48..f4b4759867bf 100644 --- a/src/libs/onboardingSelectors.ts +++ b/src/libs/onboardingSelectors.ts @@ -35,4 +35,19 @@ function hasCompletedHybridAppOnboardingFlowSelector(tryNewDotData: OnyxValue): boolean | undefined { + if (Array.isArray(onboarding)) { + return true; + } + + return onboarding?.selfTourViewed; +} + +export {hasCompletedGuidedSetupFlowSelector, hasCompletedHybridAppOnboardingFlowSelector, hasSeenTourSelector}; From 63af4e7b7f01d664e132860d71a303aeb1705f54 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Sun, 20 Oct 2024 20:30:03 +0530 Subject: [PATCH 041/438] add tour to FAB --- .../FloatingActionButtonAndPopover.tsx | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx index a49b474b185e..764095a49193 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx @@ -11,6 +11,7 @@ import * as Expensicons from '@components/Icon/Expensicons'; import type {PopoverMenuItem} from '@components/PopoverMenu'; import PopoverMenu from '@components/PopoverMenu'; import Text from '@components/Text'; +import useEnvironment from '@hooks/useEnvironment'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import usePermissions from '@hooks/usePermissions'; @@ -23,14 +24,18 @@ import interceptAnonymousUser from '@libs/interceptAnonymousUser'; import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRoute'; import Navigation from '@libs/Navigation/Navigation'; import type {CentralPaneName, NavigationPartialRoute, RootStackParamList} from '@libs/Navigation/types'; +import {hasSeenTourSelector} from '@libs/onboardingSelectors'; import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as SubscriptionUtils from '@libs/SubscriptionUtils'; +import {getNavatticURL} from '@libs/TourUtils'; import * as App from '@userActions/App'; import * as IOU from '@userActions/IOU'; +import * as Link from '@userActions/Link'; import * as Policy from '@userActions/Policy/Policy'; import * as Report from '@userActions/Report'; import * as Task from '@userActions/Task'; +import * as Welcome from '@userActions/Welcome'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -189,6 +194,12 @@ function FloatingActionButtonAndPopover( const {canUseSpotnanaTravel, canUseCombinedTrackSubmit} = usePermissions(); const canSendInvoice = useMemo(() => PolicyUtils.canSendInvoice(allPolicies as OnyxCollection, session?.email), [allPolicies, session?.email]); + const {environment} = useEnvironment(); + const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); + const navatticURL = getNavatticURL(environment, introSelected?.choice); + const [hasSeenTour = true] = useOnyx(ONYXKEYS.NVP_ONBOARDING, { + selector: hasSeenTourSelector, + }); const quickActionAvatars = useMemo(() => { if (quickActionReport) { @@ -454,7 +465,24 @@ function FloatingActionButtonAndPopover( }, ] : []), - ...(!isLoading && !Policy.hasActiveChatEnabledPolicies(allPolicies) + ...(!hasSeenTour + ? [ + { + icon: Expensicons.Tour, + displayInDefaultIconColor: true, + contentFit: 'contain' as ImageContentFit, + iconWidth: 46, + iconHeight: 40, + text: translate('tour.takeATwoMinuteTour'), + description: translate('tour.exploreExpensify'), + onSelected: () => { + Welcome.setSelfTourViewed(); + Link.openExternalLink(navatticURL); + }, + }, + ] + : []), + ...(!isLoading && !Policy.hasActiveChatEnabledPolicies(allPolicies) && hasSeenTour ? [ { displayInDefaultIconColor: true, From 7e9bf7c70414d2fdefba7a8f51d584c99fad69ce Mon Sep 17 00:00:00 2001 From: c3024 Date: Mon, 21 Oct 2024 11:35:46 +0530 Subject: [PATCH 042/438] add self guided tours to tasks --- src/CONST.ts | 17 ++++++++++++++++- src/libs/actions/Report.ts | 18 +++++++++++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 01466d8baf86..4bb90fca7aab 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -83,6 +83,13 @@ const signupQualifiers = { SMB: 'smb', } as const; +const selfGuidedTourTask: OnboardingTaskType = { + type: 'viewTour', + autoCompleted: false, + title: 'Take a 2-minute tour', + description: ({navatticURL}) => `[Take a self-guided product tour](${navatticURL}) and learn about everything Expensify has to offer.`, +}; + const onboardingEmployerOrSubmitMessage: OnboardingMessageType = { message: 'Getting paid back is as easy as sending a message. Let’s go over the basics.', video: { @@ -93,6 +100,7 @@ const onboardingEmployerOrSubmitMessage: OnboardingMessageType = { height: 960, }, tasks: [ + selfGuidedTourTask, { type: 'submitExpense', autoCompleted: false, @@ -149,11 +157,15 @@ const onboardingCompanySize = { type OnboardingInviteType = ValueOf; +type Description = + | string + | ((params: Partial<{adminsRoomLink: string; workspaceCategoriesLink: string; workspaceMoreFeaturesLink: string; workspaceMembersLink: string; navatticURL: string}>) => string); + type OnboardingTaskType = { type: string; autoCompleted: boolean; title: string; - description: string | ((params: Partial<{adminsRoomLink: string; workspaceCategoriesLink: string; workspaceMoreFeaturesLink: string; workspaceMembersLink: string}>) => string); + description: Description; }; type OnboardingMessageType = { @@ -4652,6 +4664,7 @@ const CONST = { '\n' + '*Your new workspace is ready! It’ll keep all of your spend (and chats) in one place.*', }, + selfGuidedTourTask, { type: 'meetGuide', autoCompleted: false, @@ -4748,6 +4761,7 @@ const CONST = { height: 960, }, tasks: [ + selfGuidedTourTask, { type: 'trackExpense', autoCompleted: false, @@ -4776,6 +4790,7 @@ const CONST = { height: 960, }, tasks: [ + selfGuidedTourTask, { type: 'startChat', autoCompleted: false, diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 7071c96f8612..bd237908a96e 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -58,6 +58,8 @@ import DateUtils from '@libs/DateUtils'; import {prepareDraftComment} from '@libs/DraftCommentUtils'; import * as EmojiUtils from '@libs/EmojiUtils'; import * as Environment from '@libs/Environment/Environment'; +import getEnvironment from '@libs/Environment/getEnvironment'; +import type EnvironmentType from '@libs/Environment/getEnvironment/types'; import * as ErrorUtils from '@libs/ErrorUtils'; import fileDownload from '@libs/fileDownload'; import HttpUtils from '@libs/HttpUtils'; @@ -83,6 +85,7 @@ import type {OptimisticAddCommentReportAction} from '@libs/ReportUtils'; import * as ReportUtils from '@libs/ReportUtils'; import {doesReportBelongToWorkspace} from '@libs/ReportUtils'; import shouldSkipDeepLinkNavigation from '@libs/shouldSkipDeepLinkNavigation'; +import {getNavatticURL} from '@libs/TourUtils'; import Visibility from '@libs/Visibility'; import CONFIG from '@src/CONFIG'; import type {OnboardingAccountingType, OnboardingCompanySizeType, OnboardingPurposeType} from '@src/CONST'; @@ -102,6 +105,7 @@ import type { ReportActionReactions, ReportUserIsTyping, } from '@src/types/onyx'; +import type IntroSelected from '@src/types/onyx/IntroSelected'; import type {Decision} from '@src/types/onyx/OriginalMessage'; import type {ConnectionName} from '@src/types/onyx/Policy'; import type Report from '@src/types/onyx/Report'; @@ -269,9 +273,21 @@ Onyx.connect({ callback: (value) => (allReportDraftComments = value), }); +let introSelected: IntroSelected | undefined = {}; + +Onyx.connect({ + key: ONYXKEYS.NVP_INTRO_SELECTED, + callback: (val) => (introSelected = val), +}); + let environmentURL: string; Environment.getEnvironmentURL().then((url: string) => (environmentURL = url)); +let environment: EnvironmentType; +getEnvironment().then((env) => { + environment = env; +}); + registerPaginationConfig({ initialCommand: WRITE_COMMANDS.OPEN_REPORT, previousCommand: READ_COMMANDS.GET_OLDER_ACTIONS, @@ -3359,7 +3375,6 @@ function completeOnboarding( reportComment: videoComment.commentText, }; } - const tasksData = data.tasks.map((task, index) => { const taskDescription = typeof task.description === 'function' @@ -3368,6 +3383,7 @@ function completeOnboarding( workspaceCategoriesLink: `${environmentURL}/${ROUTES.WORKSPACE_CATEGORIES.getRoute(onboardingPolicyID ?? '-1')}`, workspaceMembersLink: `${environmentURL}/${ROUTES.WORKSPACE_MEMBERS.getRoute(onboardingPolicyID ?? '-1')}`, workspaceMoreFeaturesLink: `${environmentURL}/${ROUTES.WORKSPACE_MORE_FEATURES.getRoute(onboardingPolicyID ?? '-1')}`, + navatticURL: getNavatticURL(environment, introSelected?.choice), }) : task.description; const currentTask = ReportUtils.buildOptimisticTaskReport( From ec3e59831d6353275512f404810d14fd7612c67e Mon Sep 17 00:00:00 2001 From: flaviadefaria <80457174+flaviadefaria@users.noreply.github.com> Date: Mon, 21 Oct 2024 11:36:44 +0200 Subject: [PATCH 043/438] Update Connect-To-QuickBooks-Desktop.md Adding a note before the FAQ sections so that users are reminded to store the connection password --- .../quickbooks-desktop/Connect-To-QuickBooks-Desktop.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/articles/expensify-classic/connections/quickbooks-desktop/Connect-To-QuickBooks-Desktop.md b/docs/articles/expensify-classic/connections/quickbooks-desktop/Connect-To-QuickBooks-Desktop.md index bda84eb0a49f..30785330a9ad 100644 --- a/docs/articles/expensify-classic/connections/quickbooks-desktop/Connect-To-QuickBooks-Desktop.md +++ b/docs/articles/expensify-classic/connections/quickbooks-desktop/Connect-To-QuickBooks-Desktop.md @@ -52,6 +52,10 @@ For this step, it is key to ensure that the correct company file is open in Quic ![The Web Connector pop-up, where you will need to click "Yes"](https://help.expensify.com/assets/images/QBO_desktop_07.png){:width="100%"} +{% include info.html %} +Be sure to securely save this password in a trusted password manager. You'll need it for future configuration updates or troubleshooting. Having it easily accessible will help avoid delays and ensure a smoother workflow. +{% include end-info.html %} + # FAQ ## What are the hardware and software requirements for the QuickBooks Desktop connector? From 9d66dbb9d780f8935115642e42a6fc4fcc440095 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Mon, 21 Oct 2024 12:08:59 +0200 Subject: [PATCH 044/438] fix: move offline/online at logic into seperate hook and defer values --- src/hooks/useNetwork.ts | 47 ++++----------------- src/hooks/useNetworkWithOfflineStatus.ts | 40 ++++++++++++++++++ src/pages/home/report/ReportActionsList.tsx | 4 +- 3 files changed, 51 insertions(+), 40 deletions(-) create mode 100644 src/hooks/useNetworkWithOfflineStatus.ts diff --git a/src/hooks/useNetwork.ts b/src/hooks/useNetwork.ts index 21372daf424e..950d0592b59c 100644 --- a/src/hooks/useNetwork.ts +++ b/src/hooks/useNetwork.ts @@ -1,63 +1,34 @@ -import {useContext, useEffect, useMemo, useRef, useState} from 'react'; +import {useContext, useEffect, useRef} from 'react'; import {NetworkContext} from '@components/OnyxProvider'; -import DateUtils from '@libs/DateUtils'; import CONST from '@src/CONST'; -import useLocalize from './useLocalize'; type UseNetworkProps = { onReconnect?: () => void; }; -type UseNetwork = {isOffline: boolean; lastOfflineAt?: Date; lastOnlineAt?: Date}; +type UseNetwork = {isOffline: boolean}; export default function useNetwork({onReconnect = () => {}}: UseNetworkProps = {}): UseNetwork { const callback = useRef(onReconnect); callback.current = onReconnect; - const {preferredLocale} = useLocalize(); - const {isOffline: isOfflineContext, networkStatus} = useContext(NetworkContext) ?? {...CONST.DEFAULT_NETWORK_DATA, networkStatus: CONST.NETWORK.NETWORK_STATUS.UNKNOWN}; - const prevOfflineStatusRef = useRef(isOfflineContext); + const {isOffline, networkStatus} = useContext(NetworkContext) ?? {...CONST.DEFAULT_NETWORK_DATA, networkStatus: CONST.NETWORK.NETWORK_STATUS.UNKNOWN}; + const prevOfflineStatusRef = useRef(isOffline); useEffect(() => { // If we were offline before and now we are not offline then we just reconnected - const didReconnect = prevOfflineStatusRef.current && !isOfflineContext; + const didReconnect = prevOfflineStatusRef.current && !isOffline; if (!didReconnect) { return; } callback.current(); - }, [isOfflineContext]); - - useEffect(() => { - // Used to store previous prop values to compare on next render - prevOfflineStatusRef.current = isOfflineContext; - }, [isOfflineContext]); - - // If the network status is undefined, we don't treat it as offline. Otherwise, we utilize the isOffline prop. - const isOffline = useMemo(() => (networkStatus === CONST.NETWORK.NETWORK_STATUS.UNKNOWN ? false : isOfflineContext), [isOfflineContext, networkStatus]); - - // Used to get the last time the user went offline. - // Set to a JS Date object if the user was offline before, otherwise undefined. - const [lastOfflineAt, setLastOfflineAt] = useState(() => (isOffline ? DateUtils.getLocalDateFromDatetime(preferredLocale) : undefined)); - useEffect(() => { - if (!isOffline) { - return; - } - setLastOfflineAt(DateUtils.getLocalDateFromDatetime(preferredLocale)); - // eslint-disable-next-line react-compiler/react-compiler - // eslint-disable-next-line react-hooks/exhaustive-deps }, [isOffline]); - // Used to get the last time the user went back online after being offline. - // Set to a JS Date object if the user was online before, otherwise undefined. - const [lastOnlineAt, setLastOnlineAt] = useState(() => (isOffline ? undefined : DateUtils.getLocalDateFromDatetime(preferredLocale))); useEffect(() => { - if (isOffline) { - return; - } - setLastOnlineAt(DateUtils.getLocalDateFromDatetime(preferredLocale)); - // eslint-disable-next-line react-compiler/react-compiler - // eslint-disable-next-line react-hooks/exhaustive-deps + // Used to store previous prop values to compare on next render + prevOfflineStatusRef.current = isOffline; }, [isOffline]); - return useMemo(() => ({isOffline, lastOfflineAt, lastOnlineAt}), [isOffline, lastOfflineAt, lastOnlineAt]); + // If the network status is undefined, we don't treat it as offline. Otherwise, we utilize the isOffline prop. + return {isOffline: networkStatus === CONST.NETWORK.NETWORK_STATUS.UNKNOWN ? false : isOffline}; } diff --git a/src/hooks/useNetworkWithOfflineStatus.ts b/src/hooks/useNetworkWithOfflineStatus.ts new file mode 100644 index 000000000000..5193e5a702ec --- /dev/null +++ b/src/hooks/useNetworkWithOfflineStatus.ts @@ -0,0 +1,40 @@ +import {useDeferredValue, useEffect, useMemo, useState} from 'react'; +import DateUtils from '@libs/DateUtils'; +import useLocalize from './useLocalize'; +import useNetwork from './useNetwork'; + +type UseNetworkWithOfflineStatus = {isOffline: boolean; lastOfflineAt?: Date; lastOnlineAt?: Date}; + +export default function useNetworkWithOfflineStatus(): UseNetworkWithOfflineStatus { + const {isOffline} = useNetwork(); + const {preferredLocale} = useLocalize(); + + // Used to get the last time the user went offline. + // Set to a JS Date object if the user was offline before, otherwise undefined. + const [lastOfflineAt, setLastOfflineAt] = useState(() => (isOffline ? DateUtils.getLocalDateFromDatetime(preferredLocale) : undefined)); + useEffect(() => { + if (!isOffline) { + return; + } + setLastOfflineAt(DateUtils.getLocalDateFromDatetime(preferredLocale)); + // eslint-disable-next-line react-compiler/react-compiler + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isOffline]); + + // Used to get the last time the user went back online after being offline. + // Set to a JS Date object if the user was online before, otherwise undefined. + const [lastOnlineAt, setLastOnlineAt] = useState(() => (isOffline ? undefined : DateUtils.getLocalDateFromDatetime(preferredLocale))); + useEffect(() => { + if (isOffline) { + return; + } + setLastOnlineAt(DateUtils.getLocalDateFromDatetime(preferredLocale)); + // eslint-disable-next-line react-compiler/react-compiler + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isOffline]); + + const lastOfflineAtDeferred = useDeferredValue(lastOfflineAt); + const lastOnlineAtDeferred = useDeferredValue(lastOnlineAt); + + return useMemo(() => ({isOffline, lastOfflineAt: lastOfflineAtDeferred, lastOnlineAt: lastOnlineAtDeferred}), [isOffline, lastOfflineAtDeferred, lastOnlineAtDeferred]); +} diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index 131bcb1c2ae9..bad606380138 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -13,7 +13,7 @@ import {AUTOSCROLL_TO_TOP_THRESHOLD} from '@components/InvertedFlatList/BaseInve import {usePersonalDetails} from '@components/OnyxProvider'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; -import useNetwork from '@hooks/useNetwork'; +import useNetworkWithOfflineStatus from '@hooks/useNetworkWithOfflineStatus'; import useReportScrollManager from '@hooks/useReportScrollManager'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -175,7 +175,7 @@ function ReportActionsList({ const {isInNarrowPaneModal, shouldUseNarrowLayout} = useResponsiveLayout(); const {preferredLocale} = useLocalize(); - const {isOffline, lastOfflineAt, lastOnlineAt} = useNetwork(); + const {isOffline, lastOfflineAt, lastOnlineAt} = useNetworkWithOfflineStatus(); const route = useRoute>(); const reportScrollManager = useReportScrollManager(); const userActiveSince = useRef(DateUtils.getDBTime()); From c446f1f24383908c8d267d46ca8b863ae87e8809 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Mon, 21 Oct 2024 12:35:21 +0200 Subject: [PATCH 045/438] fix: use ref instead of state for lastOffline/lastOnline at --- src/hooks/useNetworkWithOfflineStatus.ts | 18 ++++++++---------- src/pages/home/report/ReportActionsList.tsx | 5 +++-- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/hooks/useNetworkWithOfflineStatus.ts b/src/hooks/useNetworkWithOfflineStatus.ts index 5193e5a702ec..b1edf7282ee8 100644 --- a/src/hooks/useNetworkWithOfflineStatus.ts +++ b/src/hooks/useNetworkWithOfflineStatus.ts @@ -1,9 +1,10 @@ -import {useDeferredValue, useEffect, useMemo, useState} from 'react'; +import type {MutableRefObject} from 'react'; +import {useEffect, useRef} from 'react'; import DateUtils from '@libs/DateUtils'; import useLocalize from './useLocalize'; import useNetwork from './useNetwork'; -type UseNetworkWithOfflineStatus = {isOffline: boolean; lastOfflineAt?: Date; lastOnlineAt?: Date}; +type UseNetworkWithOfflineStatus = {isOffline: boolean; lastOfflineAt: MutableRefObject; lastOnlineAt: MutableRefObject}; export default function useNetworkWithOfflineStatus(): UseNetworkWithOfflineStatus { const {isOffline} = useNetwork(); @@ -11,30 +12,27 @@ export default function useNetworkWithOfflineStatus(): UseNetworkWithOfflineStat // Used to get the last time the user went offline. // Set to a JS Date object if the user was offline before, otherwise undefined. - const [lastOfflineAt, setLastOfflineAt] = useState(() => (isOffline ? DateUtils.getLocalDateFromDatetime(preferredLocale) : undefined)); + const lastOfflineAt = useRef(isOffline ? DateUtils.getLocalDateFromDatetime(preferredLocale) : undefined); useEffect(() => { if (!isOffline) { return; } - setLastOfflineAt(DateUtils.getLocalDateFromDatetime(preferredLocale)); + lastOfflineAt.current = DateUtils.getLocalDateFromDatetime(preferredLocale); // eslint-disable-next-line react-compiler/react-compiler // eslint-disable-next-line react-hooks/exhaustive-deps }, [isOffline]); // Used to get the last time the user went back online after being offline. // Set to a JS Date object if the user was online before, otherwise undefined. - const [lastOnlineAt, setLastOnlineAt] = useState(() => (isOffline ? undefined : DateUtils.getLocalDateFromDatetime(preferredLocale))); + const lastOnlineAt = useRef(isOffline ? undefined : DateUtils.getLocalDateFromDatetime(preferredLocale)); useEffect(() => { if (isOffline) { return; } - setLastOnlineAt(DateUtils.getLocalDateFromDatetime(preferredLocale)); + lastOnlineAt.current = DateUtils.getLocalDateFromDatetime(preferredLocale); // eslint-disable-next-line react-compiler/react-compiler // eslint-disable-next-line react-hooks/exhaustive-deps }, [isOffline]); - const lastOfflineAtDeferred = useDeferredValue(lastOfflineAt); - const lastOnlineAtDeferred = useDeferredValue(lastOnlineAt); - - return useMemo(() => ({isOffline, lastOfflineAt: lastOfflineAtDeferred, lastOnlineAt: lastOnlineAtDeferred}), [isOffline, lastOfflineAtDeferred, lastOnlineAtDeferred]); + return {isOffline, lastOfflineAt, lastOnlineAt}; } diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index bad606380138..6a61c263d12d 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -228,10 +228,11 @@ function ReportActionsList({ }, [report.reportID]); const wasMessageReceivedWhileOffline = useCallback( - (message: OnyxTypes.ReportAction) => !ReportActionsUtils.wasActionTakenByCurrentUser(message) && wasMessageCreatedWhileOffline(message, lastOfflineAt, lastOnlineAt, preferredLocale), + (message: OnyxTypes.ReportAction) => + !ReportActionsUtils.wasActionTakenByCurrentUser(message) && wasMessageCreatedWhileOffline(message, lastOfflineAt.current, lastOnlineAt.current, preferredLocale), // eslint-disable-next-line react-compiler/react-compiler // eslint-disable-next-line react-hooks/exhaustive-deps - [lastOfflineAt, lastOnlineAt], + [], ); /** From 87afbb89fc09ce6fb519271f1c1e96b1755ab058 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 21 Oct 2024 18:00:03 +0700 Subject: [PATCH 046/438] add accounting task --- src/CONST.ts | 55 +++++++++++++++++++++++++++++++++++--- src/libs/actions/Report.ts | 21 ++++++++++++--- 2 files changed, 68 insertions(+), 8 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 84003710938a..aa012f88f3f6 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -131,7 +131,7 @@ type OnboardingPurposeType = ValueOf; type OnboardingCompanySizeType = ValueOf; -type OnboardingAccountingType = ValueOf | null; +type OnboardingAccountingType = ValueOf | null; const onboardingInviteTypes = { IOU: 'iou', @@ -147,13 +147,37 @@ const onboardingCompanySize = { LARGE: '1001+', } as const; +const connectNames = { + QBO: 'quickbooksOnline', + XERO: 'xero', + NETSUITE: 'netsuite', + SAGE_INTACCT: 'intacct', +} as const; + type OnboardingInviteType = ValueOf; type OnboardingTaskType = { type: string; autoCompleted: boolean; - title: string; - description: string | ((params: Partial<{adminsRoomLink: string; workspaceCategoriesLink: string; workspaceMoreFeaturesLink: string; workspaceMembersLink: string}>) => string); + title: + | string + | (( + params: Partial<{ + accountingName: string; + }>, + ) => string); + description: + | string + | (( + params: Partial<{ + adminsRoomLink: string; + workspaceCategoriesLink: string; + workspaceMoreFeaturesLink: string; + workspaceMembersLink: string; + accountingName: string; + accountingLink: string; + }>, + ) => string); }; type OnboardingMessageType = { @@ -4622,7 +4646,12 @@ const CONST = { '\n' + "We'll send a request to each person so they can pay you back. Let me know if you have any questions!", }, - + ONBOARDING_ACCOUNTING_MAPPING: { + [connectNames.QBO]: 'QuickBooks Online', + [connectNames.XERO]: 'Xero', + [connectNames.NETSUITE]: 'NetSuite', + [connectNames.SAGE_INTACCT]: 'Sage Intacct', + }, ONBOARDING_MESSAGES: { [onboardingChoices.EMPLOYER]: onboardingEmployerOrSubmitMessage, [onboardingChoices.SUBMIT]: onboardingEmployerOrSubmitMessage, @@ -4734,6 +4763,24 @@ const CONST = { '\n' + `[Take me to workspace members](${workspaceMembersLink}). That’s it, happy expensing! :)`, }, + { + type: 'integration', + autoCompleted: false, + title: ({accountingName}) => `Connect to ${accountingName}`, + description: ({accountingName, accountingLink}) => + `Connect to ${accountingName} for automatic expense coding and syncing that makes month-end close a breeze.\n` + + '\n' + + `Here’s how to connect to ${accountingName}:\n` + + '\n' + + '1. Click your profile photo.\n' + + '2. Go to Workspaces.\n' + + '3. Select your workspace.\n' + + '4. Click Accounting.\n' + + `5. Find ${accountingName}.\n` + + '6. Click Connect.\n' + + '\n' + + `[Take me to Accounting!](${accountingLink})`, + }, ], }, [onboardingChoices.PERSONAL_SPEND]: { diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index acd22cacaa2b..6593f43c5532 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3326,6 +3326,7 @@ function completeOnboarding( companySize?: OnboardingCompanySizeType, userReportedIntegration?: OnboardingAccountingType, ) { + const accountingName = userReportedIntegration ? CONST.ONBOARDING_ACCOUNTING_MAPPING[userReportedIntegration] : ''; const actorAccountID = CONST.ACCOUNT_ID.CONCIERGE; const targetChatReport = ReportUtils.getChatByParticipants([actorAccountID, currentUserAccountID]); const {reportID: targetChatReportID = '', policyID: targetChatPolicyID = ''} = targetChatReport ?? {}; @@ -3360,7 +3361,7 @@ function completeOnboarding( }; } - const tasksData = data.tasks.map((task, index) => { + let tasksData = data.tasks.map((task, index) => { const taskDescription = typeof task.description === 'function' ? task.description({ @@ -3368,13 +3369,21 @@ function completeOnboarding( workspaceCategoriesLink: `${environmentURL}/${ROUTES.WORKSPACE_CATEGORIES.getRoute(onboardingPolicyID ?? '-1')}`, workspaceMembersLink: `${environmentURL}/${ROUTES.WORKSPACE_MEMBERS.getRoute(onboardingPolicyID ?? '-1')}`, workspaceMoreFeaturesLink: `${environmentURL}/${ROUTES.WORKSPACE_MORE_FEATURES.getRoute(onboardingPolicyID ?? '-1')}`, + accountingName, + accountingLink: `${environmentURL}/${ROUTES.POLICY_ACCOUNTING.getRoute(onboardingPolicyID ?? '-1')}`, }) : task.description; + const taskTitle = + typeof task.title === 'function' + ? task.title({ + accountingName, + }) + : task.title; const currentTask = ReportUtils.buildOptimisticTaskReport( actorAccountID, currentUserAccountID, targetChatReportID, - task.title, + taskTitle, taskDescription, targetChatPolicyID, CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, @@ -3382,9 +3391,9 @@ function completeOnboarding( const taskCreatedAction = ReportUtils.buildOptimisticCreatedReportAction(CONST.EMAIL.CONCIERGE); const taskReportAction = ReportUtils.buildOptimisticTaskCommentReportAction( currentTask.reportID, - task.title, + taskTitle, 0, - `task for ${task.title}`, + `task for ${taskTitle}`, targetChatReportID, actorAccountID, index + 3, @@ -3405,6 +3414,10 @@ function completeOnboarding( }; }); + if (!userReportedIntegration) { + tasksData = tasksData.filter((tasksDataItem) => tasksDataItem.task.type === 'accounting'); + } + const tasksForParameters = tasksData.map(({task, currentTask, taskCreatedAction, taskReportAction, taskDescription, completedTaskReportAction}) => ({ type: 'task', task: task.type, From e524d387f49ac2323d96237011c657c898aae627 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Mon, 21 Oct 2024 13:28:50 +0200 Subject: [PATCH 047/438] fix: move ReportAction util functions --- src/libs/ReportActionsUtils.ts | 24 +++++++++++++++--- src/pages/home/report/ReportActionsList.tsx | 28 +++------------------ 2 files changed, 24 insertions(+), 28 deletions(-) diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index beb4e591ae62..669790d2a5f6 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -9,7 +9,7 @@ import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {OnyxInputOrEntry, PrivatePersonalDetails} from '@src/types/onyx'; +import type {Locale, OnyxInputOrEntry, PrivatePersonalDetails} from '@src/types/onyx'; import type {JoinWorkspaceResolution, OriginalMessageChangeLog, OriginalMessageExportIntegration} from '@src/types/onyx/OriginalMessage'; import type Report from '@src/types/onyx/Report'; import type ReportAction from '@src/types/onyx/ReportAction'; @@ -126,7 +126,7 @@ function isDeletedAction(reportAction: OnyxInputOrEntry, lastReadTime: string) { +function isReportActionUnread(reportAction: OnyxEntry, lastReadTime?: string) { if (!lastReadTime) { return !isCreatedAction(reportAction); } @@ -1800,10 +1801,24 @@ function getReportActionsLength() { return Object.keys(allReportActions ?? {}).length; } +function wasActionCreatedWhileOffline(action: ReportAction, offlineLastAt: Date | undefined, onlineLastAt: Date | undefined, locale: Locale): boolean { + if (!onlineLastAt || !offlineLastAt) { + return false; + } + + const actionCreatedAt = DateUtils.getLocalDateFromDatetime(locale, action.created); + + if (actionCreatedAt > offlineLastAt && actionCreatedAt <= onlineLastAt) { + return true; + } + return false; +} + export { doesReportHaveVisibleActions, extractLinksFromMessageHtml, formatLastMessageText, + isReportActionUnread, getActionableMentionWhisperMessage, getAllReportActions, getCombinedReportActions, @@ -1911,6 +1926,7 @@ export { getRemovedConnectionMessage, getActionableJoinRequestPendingReportAction, getReportActionsLength, + wasActionCreatedWhileOffline, }; export type {LastVisibleMessage}; diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index 6a61c263d12d..f4f20f63d64b 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -122,27 +122,6 @@ function keyExtractor(item: OnyxTypes.ReportAction): string { return item.reportActionID; } -function wasMessageCreatedWhileOffline(message: OnyxTypes.ReportAction, offlineLastAt: Date | undefined, onlineLastAt: Date | undefined, locale: OnyxTypes.Locale): boolean { - if (!onlineLastAt || !offlineLastAt) { - return false; - } - - const messageCreatedAt = DateUtils.getLocalDateFromDatetime(locale, message.created); - - if (messageCreatedAt > offlineLastAt && messageCreatedAt <= onlineLastAt) { - return true; - } - return false; -} - -function isMessageUnread(message: OnyxTypes.ReportAction, lastReadTime?: string): boolean { - if (!lastReadTime) { - return !ReportActionsUtils.isCreatedAction(message); - } - - return !!(message && lastReadTime && message.created && lastReadTime < message.created); -} - const onScrollToIndexFailed = () => {}; function ReportActionsList({ @@ -229,7 +208,8 @@ function ReportActionsList({ const wasMessageReceivedWhileOffline = useCallback( (message: OnyxTypes.ReportAction) => - !ReportActionsUtils.wasActionTakenByCurrentUser(message) && wasMessageCreatedWhileOffline(message, lastOfflineAt.current, lastOnlineAt.current, preferredLocale), + !ReportActionsUtils.wasActionTakenByCurrentUser(message) && + ReportActionsUtils.wasActionCreatedWhileOffline(message, lastOfflineAt.current, lastOnlineAt.current, preferredLocale), // eslint-disable-next-line react-compiler/react-compiler // eslint-disable-next-line react-hooks/exhaustive-deps [], @@ -248,8 +228,8 @@ function ReportActionsList({ const isCurrentMessageOffline = wasMessageReceivedWhileOffline(message); const isNextMessageOffline = !!nextMessage && wasMessageReceivedWhileOffline(nextMessage); - const isCurrentMessageUnread = isMessageUnread(message, unreadMarkerTime); - const isNextMessageUnread = !!nextMessage && isMessageUnread(nextMessage, unreadMarkerTime); + const isCurrentMessageUnread = ReportActionsUtils.isReportActionUnread(message, unreadMarkerTime); + const isNextMessageUnread = !!nextMessage && ReportActionsUtils.isReportActionUnread(nextMessage, unreadMarkerTime); const shouldDisplayForNextMessage = isNextMessageUnread || isNextMessageOffline; const shouldDisplayBecauseOffline = isCurrentMessageOffline && !shouldDisplayForNextMessage; From 8f1dd311baa545529d38f0928555d763648b54d4 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Mon, 21 Oct 2024 15:30:01 -0400 Subject: [PATCH 048/438] add GTM lib for web --- src/libs/GoogleTagManager/index.ts | 23 +++++++++++++++++++++++ src/libs/GoogleTagManager/types.ts | 4 ++++ 2 files changed, 27 insertions(+) create mode 100644 src/libs/GoogleTagManager/index.ts create mode 100644 src/libs/GoogleTagManager/types.ts diff --git a/src/libs/GoogleTagManager/index.ts b/src/libs/GoogleTagManager/index.ts new file mode 100644 index 000000000000..65194b3ce7bb --- /dev/null +++ b/src/libs/GoogleTagManager/index.ts @@ -0,0 +1,23 @@ +import Log from '@libs/Log'; +import type {GoogleTagManagerEvent} from './types'; + +type WindowWithDataLayer = Window & { + dataLayer: { + push: (params: DataLayerPushParams) => void; + }; +}; + +type DataLayerPushParams = { + event: GoogleTagManagerEvent; + accountID: number; +}; + +declare const window: WindowWithDataLayer; + +function publishEvent(event: GoogleTagManagerEvent, accountID: number) { + window.dataLayer.push({event, accountID}); + Log.info('[GTM] event published', false, {event, accountID}); +} + +// eslint-disable-next-line import/prefer-default-export +export {publishEvent}; diff --git a/src/libs/GoogleTagManager/types.ts b/src/libs/GoogleTagManager/types.ts new file mode 100644 index 000000000000..80b4874474e8 --- /dev/null +++ b/src/libs/GoogleTagManager/types.ts @@ -0,0 +1,4 @@ +type GoogleTagManagerEvent = 'sign_up' | 'workspace_created' | 'paid_adoption'; + +// eslint-disable-next-line import/prefer-default-export +export type {GoogleTagManagerEvent}; From 5025a7ba753f73546b4fa7f13623a81549cd62e8 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Mon, 21 Oct 2024 15:31:50 -0400 Subject: [PATCH 049/438] add doc --- src/libs/GoogleTagManager/types.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libs/GoogleTagManager/types.ts b/src/libs/GoogleTagManager/types.ts index 80b4874474e8..f962e194307d 100644 --- a/src/libs/GoogleTagManager/types.ts +++ b/src/libs/GoogleTagManager/types.ts @@ -1,3 +1,7 @@ +/** + * An event that can be published to Google Tag Manager. New events must be configured in GTM before they can be used + * in the app. + */ type GoogleTagManagerEvent = 'sign_up' | 'workspace_created' | 'paid_adoption'; // eslint-disable-next-line import/prefer-default-export From 99c1ee64cf1f5feba7a693292a00939f3dd104a6 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Mon, 21 Oct 2024 15:54:10 -0400 Subject: [PATCH 050/438] upgrade react-native-firebase packages --- package-lock.json | 805 +++++++++++++++++++++++++++++++++++++--------- package.json | 10 +- 2 files changed, 663 insertions(+), 152 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8f09cb2d05a2..a6dcd7a8e04a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,10 +29,10 @@ "@react-native-clipboard/clipboard": "^1.13.2", "@react-native-community/geolocation": "3.3.0", "@react-native-community/netinfo": "11.2.1", - "@react-native-firebase/analytics": "^12.3.0", - "@react-native-firebase/app": "^12.3.0", - "@react-native-firebase/crashlytics": "^12.3.0", - "@react-native-firebase/perf": "^12.3.0", + "@react-native-firebase/analytics": "^21.1.0", + "@react-native-firebase/app": "^21.1.0", + "@react-native-firebase/crashlytics": "^21.1.0", + "@react-native-firebase/perf": "^21.1.0", "@react-native-google-signin/google-signin": "^10.0.1", "@react-native-picker/picker": "2.7.6", "@react-navigation/material-top-tabs": "^6.6.3", @@ -4198,97 +4198,6 @@ "sucrase": "3.34.0" } }, - "node_modules/@expo/config-plugins": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-8.0.4.tgz", - "integrity": "sha512-Hi+xuyNWE2LT4LVbGttHJgl9brnsdWAhEB42gWKb5+8ae86Nr/KwUBQJsJppirBYTeLjj5ZlY0glYnAkDa2jqw==", - "dependencies": { - "@expo/config-types": "^51.0.0-unreleased", - "@expo/json-file": "~8.3.0", - "@expo/plist": "^0.1.0", - "@expo/sdk-runtime-versions": "^1.0.0", - "chalk": "^4.1.2", - "debug": "^4.3.1", - "find-up": "~5.0.0", - "getenv": "^1.0.0", - "glob": "7.1.6", - "resolve-from": "^5.0.0", - "semver": "^7.5.4", - "slash": "^3.0.0", - "slugify": "^1.6.6", - "xcode": "^3.0.1", - "xml2js": "0.6.0" - } - }, - "node_modules/@expo/config-plugins/node_modules/ansi-styles": { - "version": "4.3.0", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@expo/config-plugins/node_modules/chalk": { - "version": "4.1.2", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@expo/config-plugins/node_modules/color-convert": { - "version": "2.0.1", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@expo/config-plugins/node_modules/color-name": { - "version": "1.1.4", - "license": "MIT" - }, - "node_modules/@expo/config-plugins/node_modules/has-flag": { - "version": "4.0.0", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@expo/config-plugins/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@expo/config-plugins/node_modules/supports-color": { - "version": "7.2.0", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@expo/config-types": { "version": "51.0.2", "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-51.0.2.tgz", @@ -5480,34 +5389,295 @@ "node": ">=8" } }, + "node_modules/@firebase/analytics": { + "version": "0.10.8", + "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.8.tgz", + "integrity": "sha512-CVnHcS4iRJPqtIDc411+UmFldk0ShSK3OB+D0bKD8Ck5Vro6dbK5+APZpkuWpbfdL359DIQUnAaMLE+zs/PVyA==", + "dependencies": { + "@firebase/component": "0.6.9", + "@firebase/installations": "0.6.9", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.10.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/analytics-compat": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.14.tgz", + "integrity": "sha512-unRVY6SvRqfNFIAA/kwl4vK+lvQAL2HVcgu9zTrUtTyYDmtIt/lOuHJynBMYEgLnKm39YKBDhtqdapP2e++ASw==", + "dependencies": { + "@firebase/analytics": "0.10.8", + "@firebase/analytics-types": "0.8.2", + "@firebase/component": "0.6.9", + "@firebase/util": "1.10.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/analytics-types": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.2.tgz", + "integrity": "sha512-EnzNNLh+9/sJsimsA/FGqzakmrAUKLeJvjRHlg8df1f97NLUlFidk9600y0ZgWOp3CAxn6Hjtk+08tixlUOWyw==" + }, "node_modules/@firebase/app": { - "version": "0.10.10", - "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.10.10.tgz", - "integrity": "sha512-sDqkdeFdVn5uygQm5EuIKOQ6/wxTcX/qKfm0MR46AiwLRHGLCDUMrXBkc8GhkK3ca2d6mPUSfPmndggo43D6PQ==", + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.10.11.tgz", + "integrity": "sha512-DuI8c+p/ndPmV6V0i+mcSuaU9mK9Pi9h76WOYFkPNsbmkblEy8bpTOazjG7tnfar6Of1Wn5ohvyOHSRqnN6flQ==", "dependencies": { - "@firebase/component": "0.6.8", + "@firebase/component": "0.6.9", "@firebase/logger": "0.4.2", - "@firebase/util": "1.9.7", + "@firebase/util": "1.10.0", "idb": "7.1.1", "tslib": "^2.1.0" } }, + "node_modules/@firebase/app-check": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.8.8.tgz", + "integrity": "sha512-O49RGF1xj7k6BuhxGpHmqOW5hqBIAEbt2q6POW0lIywx7emYtzPDeQI+ryQpC4zbKX646SoVZ711TN1DBLNSOQ==", + "dependencies": { + "@firebase/component": "0.6.9", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.10.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/app-check-compat": { + "version": "0.3.15", + "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.15.tgz", + "integrity": "sha512-zFIvIFFNqDXpOT2huorz9cwf56VT3oJYRFjSFYdSbGYEJYEaXjLJbfC79lx/zjx4Fh+yuN8pry3TtvwaevrGbg==", + "dependencies": { + "@firebase/app-check": "0.8.8", + "@firebase/app-check-types": "0.5.2", + "@firebase/component": "0.6.9", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.10.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/app-check-interop-types": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.2.tgz", + "integrity": "sha512-LMs47Vinv2HBMZi49C09dJxp0QT5LwDzFaVGf/+ITHe3BlIhUiLNttkATSXplc89A2lAaeTqjgqVkiRfUGyQiQ==" + }, + "node_modules/@firebase/app-check-types": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.2.tgz", + "integrity": "sha512-FSOEzTzL5bLUbD2co3Zut46iyPWML6xc4x+78TeaXMSuJap5QObfb+rVvZJtla3asN4RwU7elaQaduP+HFizDA==" + }, + "node_modules/@firebase/app-compat": { + "version": "0.2.41", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.41.tgz", + "integrity": "sha512-ktJcObWKjlIWq31kXu6sHoqWlhQD5rx0a2F2ZC2JVuEE5A5f7F43VO1Z6lfeRZXMFZbGG/aqIfXqgsP3zD2JYg==", + "dependencies": { + "@firebase/app": "0.10.11", + "@firebase/component": "0.6.9", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.10.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/app-types": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.2.tgz", + "integrity": "sha512-oMEZ1TDlBz479lmABwWsWjzHwheQKiAgnuKxE0pz0IXCVx7/rtlkx1fQ6GfgK24WCrxDKMplZrT50Kh04iMbXQ==" + }, + "node_modules/@firebase/auth": { + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.7.9.tgz", + "integrity": "sha512-yLD5095kVgDw965jepMyUrIgDklD6qH/BZNHeKOgvu7pchOKNjVM+zQoOVYJIKWMWOWBq8IRNVU6NXzBbozaJg==", + "dependencies": { + "@firebase/component": "0.6.9", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.10.0", + "tslib": "^2.1.0", + "undici": "6.19.7" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@react-native-async-storage/async-storage": "^1.18.1" + }, + "peerDependenciesMeta": { + "@react-native-async-storage/async-storage": { + "optional": true + } + } + }, + "node_modules/@firebase/auth-compat": { + "version": "0.5.14", + "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.14.tgz", + "integrity": "sha512-2eczCSqBl1KUPJacZlFpQayvpilg3dxXLy9cSMTKtQMTQSmondUtPI47P3ikH3bQAXhzKLOE+qVxJ3/IRtu9pw==", + "dependencies": { + "@firebase/auth": "1.7.9", + "@firebase/auth-types": "0.12.2", + "@firebase/component": "0.6.9", + "@firebase/util": "1.10.0", + "tslib": "^2.1.0", + "undici": "6.19.7" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/auth-interop-types": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.3.tgz", + "integrity": "sha512-Fc9wuJGgxoxQeavybiuwgyi+0rssr76b+nHpj+eGhXFYAdudMWyfBHvFL/I5fEHniUM/UQdFzi9VXJK2iZF7FQ==" + }, + "node_modules/@firebase/auth-types": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.12.2.tgz", + "integrity": "sha512-qsEBaRMoGvHO10unlDJhaKSuPn4pyoTtlQuP1ghZfzB6rNQPuhp/N/DcFZxm9i4v0SogjCbf9reWupwIvfmH6w==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, "node_modules/@firebase/component": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.8.tgz", - "integrity": "sha512-LcNvxGLLGjBwB0dJUsBGCej2fqAepWyBubs4jt1Tiuns7QLbXHuyObZ4aMeBjZjWx4m8g1LoVI9QFpSaq/k4/g==", + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.9.tgz", + "integrity": "sha512-gm8EUEJE/fEac86AvHn8Z/QW8BvR56TBw3hMW0O838J/1mThYQXAIQBgUv75EqlCZfdawpWLrKt1uXvp9ciK3Q==", + "dependencies": { + "@firebase/util": "1.10.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.8.tgz", + "integrity": "sha512-dzXALZeBI1U5TXt6619cv0+tgEhJiwlUtQ55WNZY7vGAjv7Q1QioV969iYwt1AQQ0ovHnEW0YW9TiBfefLvErg==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.2", + "@firebase/auth-interop-types": "0.2.3", + "@firebase/component": "0.6.9", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.10.0", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database-compat": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-1.0.8.tgz", + "integrity": "sha512-OpeWZoPE3sGIRPBKYnW9wLad25RaWbGyk7fFQe4xnJQKRzlynWeFBSRRAoLE2Old01WXwskUiucNqUUVlFsceg==", + "dependencies": { + "@firebase/component": "0.6.9", + "@firebase/database": "1.0.8", + "@firebase/database-types": "1.0.5", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.10.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database-types": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.5.tgz", + "integrity": "sha512-fTlqCNwFYyq/C6W7AJ5OCuq5CeZuBEsEwptnVxlNPkWCo5cTTyukzAHRSO/jaQcItz33FfYrrFk1SJofcu2AaQ==", + "dependencies": { + "@firebase/app-types": "0.9.2", + "@firebase/util": "1.10.0" + } + }, + "node_modules/@firebase/firestore": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.7.2.tgz", + "integrity": "sha512-WPkL/DEHuJg1PZPyHn81pNUhitG+7WkpLVdXmoYB23Za3eoM8VzuIn7zcD4Cji6wDCGA6eI1rvGYLtsXmE1OaQ==", "dependencies": { - "@firebase/util": "1.9.7", + "@firebase/component": "0.6.9", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.10.0", + "@firebase/webchannel-wrapper": "1.0.1", + "@grpc/grpc-js": "~1.9.0", + "@grpc/proto-loader": "^0.7.8", + "tslib": "^2.1.0", + "undici": "6.19.7" + }, + "engines": { + "node": ">=10.10.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/firestore-compat": { + "version": "0.3.37", + "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.37.tgz", + "integrity": "sha512-YwjJePx+m2OGnpKTGFTkcRXQZ+z0+8t7/zuwyOsTmKERobn0kekOv8VAQQmITcC+3du8Ul98O2a0vMH3xwt7jQ==", + "dependencies": { + "@firebase/component": "0.6.9", + "@firebase/firestore": "4.7.2", + "@firebase/firestore-types": "3.0.2", + "@firebase/util": "1.10.0", "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/firestore-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.2.tgz", + "integrity": "sha512-wp1A+t5rI2Qc/2q7r2ZpjUXkRVPtGMd6zCLsiWurjsQpqPgFin3AhNibKcIzoF2rnToNa/XYtyWXuifjOOwDgg==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" } }, + "node_modules/@firebase/functions": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.11.8.tgz", + "integrity": "sha512-Lo2rTPDn96naFIlSZKVd1yvRRqqqwiJk7cf9TZhUerwnPKgBzXy+aHE22ry+6EjCaQusUoNai6mU6p+G8QZT1g==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.2", + "@firebase/auth-interop-types": "0.2.3", + "@firebase/component": "0.6.9", + "@firebase/messaging-interop-types": "0.2.2", + "@firebase/util": "1.10.0", + "tslib": "^2.1.0", + "undici": "6.19.7" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/functions-compat": { + "version": "0.3.14", + "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.14.tgz", + "integrity": "sha512-dZ0PKOKQFnOlMfcim39XzaXonSuPPAVuzpqA4ONTIdyaJK/OnBaIEVs/+BH4faa1a2tLeR+Jy15PKqDRQoNIJw==", + "dependencies": { + "@firebase/component": "0.6.9", + "@firebase/functions": "0.11.8", + "@firebase/functions-types": "0.6.2", + "@firebase/util": "1.10.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/functions-types": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.2.tgz", + "integrity": "sha512-0KiJ9lZ28nS2iJJvimpY4nNccV21rkQyor5Iheu/nq8aKXJqtJdeSlZDspjPSBBiHRzo7/GMUttegnsEITqR+w==" + }, "node_modules/@firebase/installations": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.8.tgz", - "integrity": "sha512-57V374qdb2+wT5v7+ntpLXBjZkO6WRgmAUbVkRfFTM/4t980p0FesbqTAcOIiM8U866UeuuuF8lYH70D3jM/jQ==", + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.9.tgz", + "integrity": "sha512-hlT7AwCiKghOX3XizLxXOsTFiFCQnp/oj86zp1UxwDGmyzsyoxtX+UIZyVyH/oBF5+XtblFG9KZzZQ/h+dpy+Q==", "dependencies": { - "@firebase/component": "0.6.8", - "@firebase/util": "1.9.7", + "@firebase/component": "0.6.9", + "@firebase/util": "1.10.0", "idb": "7.1.1", "tslib": "^2.1.0" }, @@ -5515,6 +5685,29 @@ "@firebase/app": "0.x" } }, + "node_modules/@firebase/installations-compat": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.9.tgz", + "integrity": "sha512-2lfdc6kPXR7WaL4FCQSQUhXcPbI7ol3wF+vkgtU25r77OxPf8F/VmswQ7sgIkBBWtymn5ZF20TIKtnOj9rjb6w==", + "dependencies": { + "@firebase/component": "0.6.9", + "@firebase/installations": "0.6.9", + "@firebase/installations-types": "0.5.2", + "@firebase/util": "1.10.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/installations-types": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.2.tgz", + "integrity": "sha512-que84TqGRZJpJKHBlF2pkvc1YcXrtEDOVGiDjovP/a3s6W4nlbohGXEsBJo0JCeeg/UG9A+DEZVDUV9GpklUzA==", + "peerDependencies": { + "@firebase/app-types": "0.x" + } + }, "node_modules/@firebase/logger": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.2.tgz", @@ -5523,29 +5716,183 @@ "tslib": "^2.1.0" } }, + "node_modules/@firebase/messaging": { + "version": "0.12.11", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.11.tgz", + "integrity": "sha512-zn5zGhF46BmiZ7W9yAUoHlqzJGakmWn1FNp//roXHN62dgdEFIKfXY7IODA2iQiXpmUO3sBdI/Tf+Hsft1mVkw==", + "dependencies": { + "@firebase/component": "0.6.9", + "@firebase/installations": "0.6.9", + "@firebase/messaging-interop-types": "0.2.2", + "@firebase/util": "1.10.0", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/messaging-compat": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.11.tgz", + "integrity": "sha512-2NCkfE1L9jSn5OC+2n5rGAz5BEAQreK2lQGdPYQEJlAbKB2efoF+2FdiQ+LD8SlioSXz66REfeaEdesoLPFQcw==", + "dependencies": { + "@firebase/component": "0.6.9", + "@firebase/messaging": "0.12.11", + "@firebase/util": "1.10.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/messaging-interop-types": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.2.tgz", + "integrity": "sha512-l68HXbuD2PPzDUOFb3aG+nZj5KA3INcPwlocwLZOzPp9rFM9yeuI9YLl6DQfguTX5eAGxO0doTR+rDLDvQb5tA==" + }, "node_modules/@firebase/performance": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.6.8.tgz", - "integrity": "sha512-F+alziiIZ6Yn8FG47mxwljq+4XkgkT2uJIFRlkyViUQRLzrogaUJW6u/+6ZrePXnouKlKIwzqos3PVJraPEcCA==", + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.6.9.tgz", + "integrity": "sha512-PnVaak5sqfz5ivhua+HserxTJHtCar/7zM0flCX6NkzBNzJzyzlH4Hs94h2Il0LQB99roBqoE5QT1JqWqcLJHQ==", + "dependencies": { + "@firebase/component": "0.6.9", + "@firebase/installations": "0.6.9", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.10.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/performance-compat": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.9.tgz", + "integrity": "sha512-dNl95IUnpsu3fAfYBZDCVhXNkASE0uo4HYaEPd2/PKscfTvsgqFAOxfAXzBEDOnynDWiaGUnb5M1O00JQ+3FXA==", "dependencies": { - "@firebase/component": "0.6.8", - "@firebase/installations": "0.6.8", + "@firebase/component": "0.6.9", "@firebase/logger": "0.4.2", - "@firebase/util": "1.9.7", + "@firebase/performance": "0.6.9", + "@firebase/performance-types": "0.2.2", + "@firebase/util": "1.10.0", "tslib": "^2.1.0" }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/performance-types": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.2.tgz", + "integrity": "sha512-gVq0/lAClVH5STrIdKnHnCo2UcPLjJlDUoEB/tB4KM+hAeHUxWKnpT0nemUPvxZ5nbdY/pybeyMe8Cs29gEcHA==" + }, + "node_modules/@firebase/remote-config": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.4.9.tgz", + "integrity": "sha512-EO1NLCWSPMHdDSRGwZ73kxEEcTopAxX1naqLJFNApp4hO8WfKfmEpmjxmP5TrrnypjIf2tUkYaKsfbEA7+AMmA==", + "dependencies": { + "@firebase/component": "0.6.9", + "@firebase/installations": "0.6.9", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.10.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/remote-config-compat": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.9.tgz", + "integrity": "sha512-AxzGpWfWFYejH2twxfdOJt5Cfh/ATHONegTd/a0p5flEzsD5JsxXgfkFToop+mypEL3gNwawxrxlZddmDoNxyA==", + "dependencies": { + "@firebase/component": "0.6.9", + "@firebase/logger": "0.4.2", + "@firebase/remote-config": "0.4.9", + "@firebase/remote-config-types": "0.3.2", + "@firebase/util": "1.10.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/remote-config-types": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.3.2.tgz", + "integrity": "sha512-0BC4+Ud7y2aPTyhXJTMTFfrGGLqdYXrUB9sJVAB8NiqJswDTc4/2qrE/yfUbnQJhbSi6ZaTTBKyG3n1nplssaA==" + }, + "node_modules/@firebase/storage": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.13.2.tgz", + "integrity": "sha512-fxuJnHshbhVwuJ4FuISLu+/76Aby2sh+44ztjF2ppoe0TELIDxPW6/r1KGlWYt//AD0IodDYYA8ZTN89q8YqUw==", + "dependencies": { + "@firebase/component": "0.6.9", + "@firebase/util": "1.10.0", + "tslib": "^2.1.0", + "undici": "6.19.7" + }, "peerDependencies": { "@firebase/app": "0.x" } }, + "node_modules/@firebase/storage-compat": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.12.tgz", + "integrity": "sha512-hA4VWKyGU5bWOll+uwzzhEMMYGu9PlKQc1w4DWxB3aIErWYzonrZjF0icqNQZbwKNIdh8SHjZlFeB2w6OSsjfg==", + "dependencies": { + "@firebase/component": "0.6.9", + "@firebase/storage": "0.13.2", + "@firebase/storage-types": "0.8.2", + "@firebase/util": "1.10.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/storage-types": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.2.tgz", + "integrity": "sha512-0vWu99rdey0g53lA7IShoA2Lol1jfnPovzLDUBuon65K7uKG9G+L5uO05brD9pMw+l4HRFw23ah3GwTGpEav6g==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, "node_modules/@firebase/util": { - "version": "1.9.7", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.9.7.tgz", - "integrity": "sha512-fBVNH/8bRbYjqlbIhZ+lBtdAAS4WqZumx03K06/u7fJSpz1TGjEMm1ImvKD47w+xaFKIP2ori6z8BrbakRfjJA==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.10.0.tgz", + "integrity": "sha512-xKtx4A668icQqoANRxyDLBLz51TAbDP9KRfpbKGxiCAW346d0BeJe5vN6/hKxxmWwnZ0mautyv39JxviwwQMOQ==", "dependencies": { "tslib": "^2.1.0" } }, + "node_modules/@firebase/vertexai-preview": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@firebase/vertexai-preview/-/vertexai-preview-0.0.4.tgz", + "integrity": "sha512-EBSqyu9eg8frQlVU9/HjKtHN7odqbh9MtAcVz3WwHj4gLCLOoN9F/o+oxlq3CxvFrd3CNTZwu6d2mZtVlEInng==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.2", + "@firebase/component": "0.6.9", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.10.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/webchannel-wrapper": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.1.tgz", + "integrity": "sha512-jmEnr/pk0yVkA7mIlHNnxCi+wWzOFUg0WyIotgkKAb2u1J7fAeDBcVNSTjTihbAYNusCLQdW5s9IJ5qwnEufcQ==" + }, "node_modules/@formatjs/ecma402-abstract": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.0.0.tgz", @@ -5698,6 +6045,35 @@ "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, + "node_modules/@grpc/grpc-js": { + "version": "1.9.15", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.15.tgz", + "integrity": "sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ==", + "dependencies": { + "@grpc/proto-loader": "^0.7.8", + "@types/node": ">=12.12.47" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", + "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@hapi/hoek": { "version": "9.3.0", "license": "BSD-3-Clause" @@ -7442,6 +7818,60 @@ "integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==", "dev": true }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, "node_modules/@radix-ui/primitive": { "version": "1.1.0", "dev": true, @@ -9094,44 +9524,61 @@ } }, "node_modules/@react-native-firebase/analytics": { - "version": "12.9.3", - "license": "Apache-2.0", + "version": "21.1.0", + "resolved": "https://registry.npmjs.org/@react-native-firebase/analytics/-/analytics-21.1.0.tgz", + "integrity": "sha512-geaZY4OGaxDW0j+5GVKHhFPOpbHDJxhobIgRVRDZLh6ycw56pA5dYA/uKcjf+JIb/OepHs1RVBr9tVKiryb4JA==", "peerDependencies": { - "@react-native-firebase/app": "12.9.3" + "@react-native-firebase/app": "21.1.0" } }, "node_modules/@react-native-firebase/app": { - "version": "12.9.3", - "license": "Apache-2.0", + "version": "21.1.0", + "resolved": "https://registry.npmjs.org/@react-native-firebase/app/-/app-21.1.0.tgz", + "integrity": "sha512-nb31yuAUqp39YD5YftdV6sOIjB9rk6J2tKpPiZfCsF70xkRxE+aQ1FhpmlGOorunqvK5Zrsf0rJzlbvLfgHIrQ==", "dependencies": { - "@expo/config-plugins": "^4.0.3", - "opencollective-postinstall": "^2.0.1", + "firebase": "10.13.2", "superstruct": "^0.6.2" }, "peerDependencies": { + "expo": ">=47.0.0", "react": "*", "react-native": "*" + }, + "peerDependenciesMeta": { + "expo": { + "optional": true + } } }, "node_modules/@react-native-firebase/crashlytics": { - "version": "12.9.3", - "license": "Apache-2.0", + "version": "21.1.0", + "resolved": "https://registry.npmjs.org/@react-native-firebase/crashlytics/-/crashlytics-21.1.0.tgz", + "integrity": "sha512-y/6so9qdlGBnpeY0BghD8afWZipOfdZ1LhnB1YlM6GVhBvo5qudorRyLKQQLZ2xoh1PGC7M+1IW9kw/2Qu4lVQ==", "dependencies": { - "@expo/config-plugins": "^4.0.3", - "stacktrace-js": "^2.0.0" + "stacktrace-js": "^2.0.2" }, "peerDependencies": { - "@react-native-firebase/app": "12.9.3" + "@react-native-firebase/app": "21.1.0", + "expo": ">=47.0.0" + }, + "peerDependenciesMeta": { + "expo": { + "optional": true + } } }, "node_modules/@react-native-firebase/perf": { - "version": "12.9.3", - "license": "Apache-2.0", - "dependencies": { - "@expo/config-plugins": "^4.0.3" - }, + "version": "21.1.0", + "resolved": "https://registry.npmjs.org/@react-native-firebase/perf/-/perf-21.1.0.tgz", + "integrity": "sha512-u0W6RBPEOTKRzP55b7VQrF18S5YkyP/CFnuWXXoPZvNJTJDcH/68AH2Ih0a+tFths/3ZXDNzVhNE+03BV5uWqA==", "peerDependencies": { - "@react-native-firebase/app": "12.9.3" + "@react-native-firebase/app": "21.1.0", + "expo": ">=47.0.0" + }, + "peerDependenciesMeta": { + "expo": { + "optional": true + } } }, "node_modules/@react-native-google-signin/google-signin": { @@ -24675,7 +25122,6 @@ }, "node_modules/faye-websocket": { "version": "0.11.4", - "dev": true, "license": "Apache-2.0", "dependencies": { "websocket-driver": ">=0.5.1" @@ -24967,6 +25413,40 @@ "micromatch": "^4.0.2" } }, + "node_modules/firebase": { + "version": "10.13.2", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-10.13.2.tgz", + "integrity": "sha512-YeI+TO5rJsoyZsVFx9WiN5ibdVCIigYTWwldRTMfCzrSPrJFVGao4acYj3x0EYGKDIgSgEyVBayD5BffD4Eyow==", + "dependencies": { + "@firebase/analytics": "0.10.8", + "@firebase/analytics-compat": "0.2.14", + "@firebase/app": "0.10.11", + "@firebase/app-check": "0.8.8", + "@firebase/app-check-compat": "0.3.15", + "@firebase/app-compat": "0.2.41", + "@firebase/app-types": "0.9.2", + "@firebase/auth": "1.7.9", + "@firebase/auth-compat": "0.5.14", + "@firebase/database": "1.0.8", + "@firebase/database-compat": "1.0.8", + "@firebase/firestore": "4.7.2", + "@firebase/firestore-compat": "0.3.37", + "@firebase/functions": "0.11.8", + "@firebase/functions-compat": "0.3.14", + "@firebase/installations": "0.6.9", + "@firebase/installations-compat": "0.2.9", + "@firebase/messaging": "0.12.11", + "@firebase/messaging-compat": "0.2.11", + "@firebase/performance": "0.6.9", + "@firebase/performance-compat": "0.2.9", + "@firebase/remote-config": "0.4.9", + "@firebase/remote-config-compat": "0.2.9", + "@firebase/storage": "0.13.2", + "@firebase/storage-compat": "0.3.12", + "@firebase/util": "1.10.0", + "@firebase/vertexai-preview": "0.0.4" + } + }, "node_modules/flat-cache": { "version": "3.0.4", "dev": true, @@ -26171,7 +26651,6 @@ }, "node_modules/http-parser-js": { "version": "0.5.8", - "dev": true, "license": "MIT" }, "node_modules/http-proxy": { @@ -30460,6 +30939,11 @@ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, "node_modules/lodash.clonedeep": { "version": "4.5.0", "dev": true, @@ -30602,6 +31086,11 @@ "logkitty": "bin/logkitty.js" } }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, "node_modules/loose-envify": { "version": "1.4.0", "license": "MIT", @@ -32725,13 +33214,6 @@ "undici-types": "~5.26.4" } }, - "node_modules/opencollective-postinstall": { - "version": "2.0.3", - "license": "MIT", - "bin": { - "opencollective-postinstall": "index.js" - } - }, "node_modules/opener": { "version": "1.5.2", "dev": true, @@ -33774,6 +34256,29 @@ "node": ">= 8" } }, + "node_modules/protobufjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", + "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/protocol-buffers-schema": { "version": "3.6.0", "license": "MIT" @@ -40141,6 +40646,14 @@ "version": "1.13.6", "license": "MIT" }, + "node_modules/undici": { + "version": "6.19.7", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.19.7.tgz", + "integrity": "sha512-HR3W/bMGPSr90i8AAp2C4DM3wChFdJPLrWYpIS++LxS8K+W535qftjt+4MyjNYHeWabMj1nvtmLIi7l++iq91A==", + "engines": { + "node": ">=18.17" + } + }, "node_modules/undici-types": { "version": "5.26.5", "license": "MIT" @@ -41364,7 +41877,6 @@ }, "node_modules/websocket-driver": { "version": "0.7.4", - "dev": true, "license": "Apache-2.0", "dependencies": { "http-parser-js": ">=0.5.1", @@ -41377,7 +41889,6 @@ }, "node_modules/websocket-extensions": { "version": "0.1.4", - "dev": true, "license": "Apache-2.0", "engines": { "node": ">=0.8.0" diff --git a/package.json b/package.json index 950b12b3a008..d0cc6bb5b1f3 100644 --- a/package.json +++ b/package.json @@ -84,10 +84,10 @@ "@react-native-clipboard/clipboard": "^1.13.2", "@react-native-community/geolocation": "3.3.0", "@react-native-community/netinfo": "11.2.1", - "@react-native-firebase/analytics": "^12.3.0", - "@react-native-firebase/app": "^12.3.0", - "@react-native-firebase/crashlytics": "^12.3.0", - "@react-native-firebase/perf": "^12.3.0", + "@react-native-firebase/analytics": "^21.1.0", + "@react-native-firebase/app": "^21.1.0", + "@react-native-firebase/crashlytics": "^21.1.0", + "@react-native-firebase/perf": "^21.1.0", "@react-native-google-signin/google-signin": "^10.0.1", "@react-native-picker/picker": "2.7.6", "@react-navigation/material-top-tabs": "^6.6.3", @@ -378,4 +378,4 @@ "node": "20.15.1", "npm": "10.7.0" } -} +} \ No newline at end of file From 7d29053ede4da640f9bf6a0a2da86009f93027f4 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Mon, 21 Oct 2024 15:54:24 -0400 Subject: [PATCH 051/438] update patches --- ...t-native-firebase+analytics+21.1.0+001+hybrid-app.patch} | 6 +++--- ... @react-native-firebase+app+21.1.0+001+hybrid-app.patch} | 0 ...native-firebase+crashlytics+21.1.0+001+hybrid-app.patch} | 0 ...@react-native-firebase+perf+21.1.0+001+hybrid-app.patch} | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename patches/{@react-native-firebase+analytics+12.9.3+001+hybrid-app.patch => @react-native-firebase+analytics+21.1.0+001+hybrid-app.patch} (81%) rename patches/{@react-native-firebase+app+12.9.3+001+hybrid-app.patch => @react-native-firebase+app+21.1.0+001+hybrid-app.patch} (100%) rename patches/{@react-native-firebase+crashlytics+12.9.3+001+hybrid-app.patch => @react-native-firebase+crashlytics+21.1.0+001+hybrid-app.patch} (100%) rename patches/{@react-native-firebase+perf+12.9.3+001+hybrid-app.patch => @react-native-firebase+perf+21.1.0+001+hybrid-app.patch} (100%) diff --git a/patches/@react-native-firebase+analytics+12.9.3+001+hybrid-app.patch b/patches/@react-native-firebase+analytics+21.1.0+001+hybrid-app.patch similarity index 81% rename from patches/@react-native-firebase+analytics+12.9.3+001+hybrid-app.patch rename to patches/@react-native-firebase+analytics+21.1.0+001+hybrid-app.patch index 74d3e2a8005a..19464c3817d8 100644 --- a/patches/@react-native-firebase+analytics+12.9.3+001+hybrid-app.patch +++ b/patches/@react-native-firebase+analytics+21.1.0+001+hybrid-app.patch @@ -1,8 +1,8 @@ diff --git a/node_modules/@react-native-firebase/analytics/android/build.gradle b/node_modules/@react-native-firebase/analytics/android/build.gradle -index d223ebf..821b730 100644 +index 5cd04f1..ab7ea07 100644 --- a/node_modules/@react-native-firebase/analytics/android/build.gradle +++ b/node_modules/@react-native-firebase/analytics/android/build.gradle -@@ -45,6 +45,8 @@ if (coreVersionDetected != coreVersionRequired) { +@@ -44,6 +44,8 @@ if (coreVersionDetected != coreVersionRequired) { } } @@ -11,7 +11,7 @@ index d223ebf..821b730 100644 project.ext { set('react-native', [ versions: [ -@@ -144,4 +146,3 @@ dependencies { +@@ -175,4 +177,3 @@ dependencies { ReactNative.shared.applyPackageVersion() ReactNative.shared.applyDefaultExcludes() ReactNative.module.applyAndroidVersions() diff --git a/patches/@react-native-firebase+app+12.9.3+001+hybrid-app.patch b/patches/@react-native-firebase+app+21.1.0+001+hybrid-app.patch similarity index 100% rename from patches/@react-native-firebase+app+12.9.3+001+hybrid-app.patch rename to patches/@react-native-firebase+app+21.1.0+001+hybrid-app.patch diff --git a/patches/@react-native-firebase+crashlytics+12.9.3+001+hybrid-app.patch b/patches/@react-native-firebase+crashlytics+21.1.0+001+hybrid-app.patch similarity index 100% rename from patches/@react-native-firebase+crashlytics+12.9.3+001+hybrid-app.patch rename to patches/@react-native-firebase+crashlytics+21.1.0+001+hybrid-app.patch diff --git a/patches/@react-native-firebase+perf+12.9.3+001+hybrid-app.patch b/patches/@react-native-firebase+perf+21.1.0+001+hybrid-app.patch similarity index 100% rename from patches/@react-native-firebase+perf+12.9.3+001+hybrid-app.patch rename to patches/@react-native-firebase+perf+21.1.0+001+hybrid-app.patch From 411de4269e66ce2a8f93df4811614c70ae9d508b Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Mon, 21 Oct 2024 16:10:16 -0400 Subject: [PATCH 052/438] add GoogleTagManagerModule type --- src/libs/GoogleTagManager/index.ts | 8 ++++++-- src/libs/GoogleTagManager/types.ts | 7 ++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/libs/GoogleTagManager/index.ts b/src/libs/GoogleTagManager/index.ts index 65194b3ce7bb..7614e85abb99 100644 --- a/src/libs/GoogleTagManager/index.ts +++ b/src/libs/GoogleTagManager/index.ts @@ -1,5 +1,6 @@ import Log from '@libs/Log'; import type {GoogleTagManagerEvent} from './types'; +import type GoogleTagManagerModule from './types'; type WindowWithDataLayer = Window & { dataLayer: { @@ -19,5 +20,8 @@ function publishEvent(event: GoogleTagManagerEvent, accountID: number) { Log.info('[GTM] event published', false, {event, accountID}); } -// eslint-disable-next-line import/prefer-default-export -export {publishEvent}; +const GoogleTagManager: GoogleTagManagerModule = { + publishEvent, +}; + +export default GoogleTagManager; diff --git a/src/libs/GoogleTagManager/types.ts b/src/libs/GoogleTagManager/types.ts index f962e194307d..a4af6aa00330 100644 --- a/src/libs/GoogleTagManager/types.ts +++ b/src/libs/GoogleTagManager/types.ts @@ -4,5 +4,10 @@ */ type GoogleTagManagerEvent = 'sign_up' | 'workspace_created' | 'paid_adoption'; -// eslint-disable-next-line import/prefer-default-export +type GoogleTagManagerModule = { + publishEvent: (event: GoogleTagManagerEvent, accountID: number) => void; +}; + +export default GoogleTagManagerModule; + export type {GoogleTagManagerEvent}; From 4e8fdc7697b9c0e6ed739a79181c6101c9051fb5 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Mon, 21 Oct 2024 16:14:13 -0400 Subject: [PATCH 053/438] add GTM lib for native --- src/libs/GoogleTagManager/index.native.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/libs/GoogleTagManager/index.native.ts diff --git a/src/libs/GoogleTagManager/index.native.ts b/src/libs/GoogleTagManager/index.native.ts new file mode 100644 index 000000000000..267591d6ef40 --- /dev/null +++ b/src/libs/GoogleTagManager/index.native.ts @@ -0,0 +1,15 @@ +import analytics from '@react-native-firebase/analytics'; +import Log from '@libs/Log'; +import type {GoogleTagManagerEvent} from './types'; +import type GoogleTagManagerModule from './types'; + +function publishEvent(event: GoogleTagManagerEvent, accountID: number) { + analytics().logEvent(event, {accountID}); + Log.info('[GTM] event published', false, {event, accountID}); +} + +const GoogleTagManager: GoogleTagManagerModule = { + publishEvent, +}; + +export default GoogleTagManager; From f5cc230c1369f9b6e29f0c04940243dd2451fa8b Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Mon, 21 Oct 2024 16:18:11 -0400 Subject: [PATCH 054/438] add doc --- src/libs/GoogleTagManager/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libs/GoogleTagManager/index.ts b/src/libs/GoogleTagManager/index.ts index 7614e85abb99..dec06a6bd157 100644 --- a/src/libs/GoogleTagManager/index.ts +++ b/src/libs/GoogleTagManager/index.ts @@ -2,6 +2,9 @@ import Log from '@libs/Log'; import type {GoogleTagManagerEvent} from './types'; import type GoogleTagManagerModule from './types'; +/** + * The dataLayer is added with a js snippet from Google in web/thirdPartyScripts.js + */ type WindowWithDataLayer = Window & { dataLayer: { push: (params: DataLayerPushParams) => void; From 494ad4dde8a96538d1cde76f414dd3a434b2d80b Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 22 Oct 2024 10:46:39 +0700 Subject: [PATCH 055/438] update type of accounting task --- src/CONST.ts | 2 +- src/libs/actions/Report.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 6e923b18ef78..f381ea8c9a1c 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -4789,7 +4789,7 @@ const CONST = { `[Take me to workspace members](${workspaceMembersLink}). That’s it, happy expensing! :)`, }, { - type: 'integration', + type: 'addAccountingIntegration', autoCompleted: false, title: ({accountingName}) => `Connect to ${accountingName}`, description: ({accountingName, accountingLink}) => diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 1ba2fe85e82f..5bc79db0abb8 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3420,7 +3420,7 @@ function completeOnboarding( }); if (!userReportedIntegration) { - tasksData = tasksData.filter((tasksDataItem) => tasksDataItem.task.type === 'accounting'); + tasksData = tasksData.filter((tasksDataItem) => tasksDataItem.task.type !== 'addAccountingIntegration'); } const tasksForParameters = tasksData.map(({task, currentTask, taskCreatedAction, taskReportAction, taskDescription, completedTaskReportAction}) => ({ From cbf0885e126eb18a6b64f05d58c7c3d2261b5589 Mon Sep 17 00:00:00 2001 From: truph01 Date: Tue, 22 Oct 2024 15:44:15 +0700 Subject: [PATCH 056/438] fix: hide Add shipping details if deactivated --- .../ReportActionItem/IssueCardMessage.tsx | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/IssueCardMessage.tsx b/src/components/ReportActionItem/IssueCardMessage.tsx index c9fa9a6f2df0..17b445a052c7 100644 --- a/src/components/ReportActionItem/IssueCardMessage.tsx +++ b/src/components/ReportActionItem/IssueCardMessage.tsx @@ -6,6 +6,7 @@ import RenderHTML from '@components/RenderHTML'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; +import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -24,6 +25,9 @@ function IssueCardMessage({action, policyID}: IssueCardMessageProps) { const styles = useThemeStyles(); const [privatePersonalDetails] = useOnyx(ONYXKEYS.PRIVATE_PERSONAL_DETAILS); const [session] = useOnyx(ONYXKEYS.SESSION); + const workspaceAccountID = PolicyUtils.getWorkspaceAccountID(policyID ?? '-1'); + const [cardList = {}] = useOnyx(ONYXKEYS.CARD_LIST); + const [cardsList] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${CONST.EXPENSIFY_CARD.BANK}`); const assigneeAccountID = (ReportActionsUtils.getOriginalMessage(action) as IssueNewCardOriginalMessage)?.assigneeAccountID; @@ -37,7 +41,18 @@ function IssueCardMessage({action, policyID}: IssueCardMessageProps) { const isAssigneeCurrentUser = !isEmptyObject(session) && session.accountID === assigneeAccountID; - const shouldShowAddMissingDetailsButton = action?.actionName === CONST.REPORT.ACTIONS.TYPE.CARD_MISSING_ADDRESS && missingDetails && isAssigneeCurrentUser; + const cardIssuedActionOriginalMessage = ReportActionsUtils.isActionOfType( + action, + CONST.REPORT.ACTIONS.TYPE.CARD_ISSUED, + CONST.REPORT.ACTIONS.TYPE.CARD_ISSUED_VIRTUAL, + CONST.REPORT.ACTIONS.TYPE.CARD_MISSING_ADDRESS, + ) + ? ReportActionsUtils.getOriginalMessage(action) + : undefined; + const cardID = cardIssuedActionOriginalMessage?.cardID ?? -1; + const isPolicyAdmin = PolicyUtils.isPolicyAdmin(PolicyUtils.getPolicy(policyID)); + const card = isPolicyAdmin ? cardsList?.[cardID] : cardList[cardID]; + const shouldShowAddMissingDetailsButton = !isEmptyObject(card) && action?.actionName === CONST.REPORT.ACTIONS.TYPE.CARD_MISSING_ADDRESS && missingDetails && isAssigneeCurrentUser; return ( <> From 28940c2602ead14d427cb3a726287edf2828f8a9 Mon Sep 17 00:00:00 2001 From: daledah Date: Tue, 22 Oct 2024 19:05:34 +0700 Subject: [PATCH 057/438] fix: app crash when opening split view --- src/components/MoneyRequestConfirmationList.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index a46b9d62dfde..df12ec986aa9 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -159,7 +159,7 @@ function MoneyRequestConfirmationList({ payeePersonalDetails: payeePersonalDetailsProp, isReadOnly = false, bankAccountRoute = '', - policyID = '', + policyID, reportID = '', receiptPath = '', iouComment, @@ -174,14 +174,14 @@ function MoneyRequestConfirmationList({ shouldPlaySound = true, isConfirmed, }: MoneyRequestConfirmationListProps) { - const [policyCategoriesReal] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`); - const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`); - const [policyReal] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); - const [policyDraft] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${policyID}`); - const [defaultMileageRate] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${policyID}`, { + const [policyCategoriesReal] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID ?? '-1'}`); + const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID ?? '-1'}`); + const [policyReal] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID ?? '-1'}`); + const [policyDraft] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${policyID ?? '-1'}`); + const [defaultMileageRate] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${policyID ?? '-1'}`, { selector: (selectedPolicy) => DistanceRequestUtils.getDefaultMileageRate(selectedPolicy), }); - const [policyCategoriesDraft] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES_DRAFT}${policyID}`); + const [policyCategoriesDraft] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES_DRAFT}${policyID ?? '-1'}`); const [lastSelectedDistanceRates] = useOnyx(ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES); const [currencyList] = useOnyx(ONYXKEYS.CURRENCY_LIST); const policy = policyReal ?? policyDraft; From 3a06c8533bfad4683ff83d93671a760003301152 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Tue, 22 Oct 2024 18:26:02 +0200 Subject: [PATCH 058/438] fix: fix and simplify offline message check --- src/hooks/useNetworkWithOfflineStatus.ts | 20 ++++++-------------- src/libs/ReportActionsUtils.ts | 20 +++++++++++++++++--- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/hooks/useNetworkWithOfflineStatus.ts b/src/hooks/useNetworkWithOfflineStatus.ts index b1edf7282ee8..7d5d11261676 100644 --- a/src/hooks/useNetworkWithOfflineStatus.ts +++ b/src/hooks/useNetworkWithOfflineStatus.ts @@ -10,26 +10,18 @@ export default function useNetworkWithOfflineStatus(): UseNetworkWithOfflineStat const {isOffline} = useNetwork(); const {preferredLocale} = useLocalize(); - // Used to get the last time the user went offline. - // Set to a JS Date object if the user was offline before, otherwise undefined. + // The last time/date the user went/was offline. If the user was never offline, it is set to undefined. const lastOfflineAt = useRef(isOffline ? DateUtils.getLocalDateFromDatetime(preferredLocale) : undefined); - useEffect(() => { - if (!isOffline) { - return; - } - lastOfflineAt.current = DateUtils.getLocalDateFromDatetime(preferredLocale); - // eslint-disable-next-line react-compiler/react-compiler - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isOffline]); - // Used to get the last time the user went back online after being offline. - // Set to a JS Date object if the user was online before, otherwise undefined. + // The last time/date the user went/was online. If the user was never online, it is set to undefined. const lastOnlineAt = useRef(isOffline ? undefined : DateUtils.getLocalDateFromDatetime(preferredLocale)); + useEffect(() => { if (isOffline) { - return; + lastOfflineAt.current = DateUtils.getLocalDateFromDatetime(preferredLocale); + } else { + lastOnlineAt.current = DateUtils.getLocalDateFromDatetime(preferredLocale); } - lastOnlineAt.current = DateUtils.getLocalDateFromDatetime(preferredLocale); // eslint-disable-next-line react-compiler/react-compiler // eslint-disable-next-line react-hooks/exhaustive-deps }, [isOffline]); diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 36f7ae3d8442..c8963d134616 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -1803,16 +1803,30 @@ function getReportActionsLength() { return Object.keys(allReportActions ?? {}).length; } -function wasActionCreatedWhileOffline(action: ReportAction, offlineLastAt: Date | undefined, onlineLastAt: Date | undefined, locale: Locale): boolean { - if (!onlineLastAt || !offlineLastAt) { +function wasActionCreatedWhileOffline(action: ReportAction, isOffline: boolean, lastOfflineAt: Date | undefined, lastOnlineAt: Date | undefined, locale: Locale): boolean { + // The user was never online. + if (!lastOnlineAt) { + return true; + } + + // The user never was never offline. + if (!lastOfflineAt) { return false; } const actionCreatedAt = DateUtils.getLocalDateFromDatetime(locale, action.created); - if (actionCreatedAt > offlineLastAt && actionCreatedAt <= onlineLastAt) { + // The action was created before the user went offline. + if (actionCreatedAt <= lastOfflineAt) { + return false; + } + + // The action was created while the user was offline. + if (isOffline || actionCreatedAt < lastOnlineAt) { return true; } + + // The action was created after the user went back online. return false; } From 91bbc8fca12132cb062adcc72f996824a0cb0445 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Tue, 22 Oct 2024 18:27:02 +0200 Subject: [PATCH 059/438] fix: wrong parameters --- src/pages/home/report/ReportActionsList.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index f4f20f63d64b..80b046632784 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -209,10 +209,8 @@ function ReportActionsList({ const wasMessageReceivedWhileOffline = useCallback( (message: OnyxTypes.ReportAction) => !ReportActionsUtils.wasActionTakenByCurrentUser(message) && - ReportActionsUtils.wasActionCreatedWhileOffline(message, lastOfflineAt.current, lastOnlineAt.current, preferredLocale), - // eslint-disable-next-line react-compiler/react-compiler - // eslint-disable-next-line react-hooks/exhaustive-deps - [], + ReportActionsUtils.wasActionCreatedWhileOffline(message, isOffline, lastOfflineAt.current, lastOnlineAt.current, preferredLocale), + [isOffline, lastOfflineAt, lastOnlineAt, preferredLocale], ); /** From 4cf4ff548b01be0a2e57842876f0b99907daa9a7 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Tue, 22 Oct 2024 18:33:14 +0200 Subject: [PATCH 060/438] fix: remove dependency array eslint exception --- src/hooks/useNetworkWithOfflineStatus.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/hooks/useNetworkWithOfflineStatus.ts b/src/hooks/useNetworkWithOfflineStatus.ts index 7d5d11261676..9167b0cae969 100644 --- a/src/hooks/useNetworkWithOfflineStatus.ts +++ b/src/hooks/useNetworkWithOfflineStatus.ts @@ -22,9 +22,7 @@ export default function useNetworkWithOfflineStatus(): UseNetworkWithOfflineStat } else { lastOnlineAt.current = DateUtils.getLocalDateFromDatetime(preferredLocale); } - // eslint-disable-next-line react-compiler/react-compiler - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isOffline]); + }, [isOffline, preferredLocale]); return {isOffline, lastOfflineAt, lastOnlineAt}; } From e6fd09ec4a05a919e884571c18fc5704a28b8563 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Tue, 22 Oct 2024 16:28:57 -0400 Subject: [PATCH 061/438] Revert "upgrade react-native-firebase packages" This reverts commit 99c1ee64cf1f5feba7a693292a00939f3dd104a6. --- package-lock.json | 805 +++++++++------------------------------------- package.json | 10 +- 2 files changed, 152 insertions(+), 663 deletions(-) diff --git a/package-lock.json b/package-lock.json index a6dcd7a8e04a..8f09cb2d05a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,10 +29,10 @@ "@react-native-clipboard/clipboard": "^1.13.2", "@react-native-community/geolocation": "3.3.0", "@react-native-community/netinfo": "11.2.1", - "@react-native-firebase/analytics": "^21.1.0", - "@react-native-firebase/app": "^21.1.0", - "@react-native-firebase/crashlytics": "^21.1.0", - "@react-native-firebase/perf": "^21.1.0", + "@react-native-firebase/analytics": "^12.3.0", + "@react-native-firebase/app": "^12.3.0", + "@react-native-firebase/crashlytics": "^12.3.0", + "@react-native-firebase/perf": "^12.3.0", "@react-native-google-signin/google-signin": "^10.0.1", "@react-native-picker/picker": "2.7.6", "@react-navigation/material-top-tabs": "^6.6.3", @@ -4198,6 +4198,97 @@ "sucrase": "3.34.0" } }, + "node_modules/@expo/config-plugins": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-8.0.4.tgz", + "integrity": "sha512-Hi+xuyNWE2LT4LVbGttHJgl9brnsdWAhEB42gWKb5+8ae86Nr/KwUBQJsJppirBYTeLjj5ZlY0glYnAkDa2jqw==", + "dependencies": { + "@expo/config-types": "^51.0.0-unreleased", + "@expo/json-file": "~8.3.0", + "@expo/plist": "^0.1.0", + "@expo/sdk-runtime-versions": "^1.0.0", + "chalk": "^4.1.2", + "debug": "^4.3.1", + "find-up": "~5.0.0", + "getenv": "^1.0.0", + "glob": "7.1.6", + "resolve-from": "^5.0.0", + "semver": "^7.5.4", + "slash": "^3.0.0", + "slugify": "^1.6.6", + "xcode": "^3.0.1", + "xml2js": "0.6.0" + } + }, + "node_modules/@expo/config-plugins/node_modules/ansi-styles": { + "version": "4.3.0", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@expo/config-plugins/node_modules/chalk": { + "version": "4.1.2", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@expo/config-plugins/node_modules/color-convert": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@expo/config-plugins/node_modules/color-name": { + "version": "1.1.4", + "license": "MIT" + }, + "node_modules/@expo/config-plugins/node_modules/has-flag": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@expo/config-plugins/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@expo/config-plugins/node_modules/supports-color": { + "version": "7.2.0", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@expo/config-types": { "version": "51.0.2", "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-51.0.2.tgz", @@ -5389,295 +5480,34 @@ "node": ">=8" } }, - "node_modules/@firebase/analytics": { - "version": "0.10.8", - "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.8.tgz", - "integrity": "sha512-CVnHcS4iRJPqtIDc411+UmFldk0ShSK3OB+D0bKD8Ck5Vro6dbK5+APZpkuWpbfdL359DIQUnAaMLE+zs/PVyA==", - "dependencies": { - "@firebase/component": "0.6.9", - "@firebase/installations": "0.6.9", - "@firebase/logger": "0.4.2", - "@firebase/util": "1.10.0", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app": "0.x" - } - }, - "node_modules/@firebase/analytics-compat": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.14.tgz", - "integrity": "sha512-unRVY6SvRqfNFIAA/kwl4vK+lvQAL2HVcgu9zTrUtTyYDmtIt/lOuHJynBMYEgLnKm39YKBDhtqdapP2e++ASw==", - "dependencies": { - "@firebase/analytics": "0.10.8", - "@firebase/analytics-types": "0.8.2", - "@firebase/component": "0.6.9", - "@firebase/util": "1.10.0", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app-compat": "0.x" - } - }, - "node_modules/@firebase/analytics-types": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.2.tgz", - "integrity": "sha512-EnzNNLh+9/sJsimsA/FGqzakmrAUKLeJvjRHlg8df1f97NLUlFidk9600y0ZgWOp3CAxn6Hjtk+08tixlUOWyw==" - }, "node_modules/@firebase/app": { - "version": "0.10.11", - "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.10.11.tgz", - "integrity": "sha512-DuI8c+p/ndPmV6V0i+mcSuaU9mK9Pi9h76WOYFkPNsbmkblEy8bpTOazjG7tnfar6Of1Wn5ohvyOHSRqnN6flQ==", + "version": "0.10.10", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.10.10.tgz", + "integrity": "sha512-sDqkdeFdVn5uygQm5EuIKOQ6/wxTcX/qKfm0MR46AiwLRHGLCDUMrXBkc8GhkK3ca2d6mPUSfPmndggo43D6PQ==", "dependencies": { - "@firebase/component": "0.6.9", + "@firebase/component": "0.6.8", "@firebase/logger": "0.4.2", - "@firebase/util": "1.10.0", + "@firebase/util": "1.9.7", "idb": "7.1.1", "tslib": "^2.1.0" } }, - "node_modules/@firebase/app-check": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.8.8.tgz", - "integrity": "sha512-O49RGF1xj7k6BuhxGpHmqOW5hqBIAEbt2q6POW0lIywx7emYtzPDeQI+ryQpC4zbKX646SoVZ711TN1DBLNSOQ==", - "dependencies": { - "@firebase/component": "0.6.9", - "@firebase/logger": "0.4.2", - "@firebase/util": "1.10.0", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app": "0.x" - } - }, - "node_modules/@firebase/app-check-compat": { - "version": "0.3.15", - "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.15.tgz", - "integrity": "sha512-zFIvIFFNqDXpOT2huorz9cwf56VT3oJYRFjSFYdSbGYEJYEaXjLJbfC79lx/zjx4Fh+yuN8pry3TtvwaevrGbg==", - "dependencies": { - "@firebase/app-check": "0.8.8", - "@firebase/app-check-types": "0.5.2", - "@firebase/component": "0.6.9", - "@firebase/logger": "0.4.2", - "@firebase/util": "1.10.0", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app-compat": "0.x" - } - }, - "node_modules/@firebase/app-check-interop-types": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.2.tgz", - "integrity": "sha512-LMs47Vinv2HBMZi49C09dJxp0QT5LwDzFaVGf/+ITHe3BlIhUiLNttkATSXplc89A2lAaeTqjgqVkiRfUGyQiQ==" - }, - "node_modules/@firebase/app-check-types": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.2.tgz", - "integrity": "sha512-FSOEzTzL5bLUbD2co3Zut46iyPWML6xc4x+78TeaXMSuJap5QObfb+rVvZJtla3asN4RwU7elaQaduP+HFizDA==" - }, - "node_modules/@firebase/app-compat": { - "version": "0.2.41", - "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.41.tgz", - "integrity": "sha512-ktJcObWKjlIWq31kXu6sHoqWlhQD5rx0a2F2ZC2JVuEE5A5f7F43VO1Z6lfeRZXMFZbGG/aqIfXqgsP3zD2JYg==", - "dependencies": { - "@firebase/app": "0.10.11", - "@firebase/component": "0.6.9", - "@firebase/logger": "0.4.2", - "@firebase/util": "1.10.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@firebase/app-types": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.2.tgz", - "integrity": "sha512-oMEZ1TDlBz479lmABwWsWjzHwheQKiAgnuKxE0pz0IXCVx7/rtlkx1fQ6GfgK24WCrxDKMplZrT50Kh04iMbXQ==" - }, - "node_modules/@firebase/auth": { - "version": "1.7.9", - "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.7.9.tgz", - "integrity": "sha512-yLD5095kVgDw965jepMyUrIgDklD6qH/BZNHeKOgvu7pchOKNjVM+zQoOVYJIKWMWOWBq8IRNVU6NXzBbozaJg==", - "dependencies": { - "@firebase/component": "0.6.9", - "@firebase/logger": "0.4.2", - "@firebase/util": "1.10.0", - "tslib": "^2.1.0", - "undici": "6.19.7" - }, - "peerDependencies": { - "@firebase/app": "0.x", - "@react-native-async-storage/async-storage": "^1.18.1" - }, - "peerDependenciesMeta": { - "@react-native-async-storage/async-storage": { - "optional": true - } - } - }, - "node_modules/@firebase/auth-compat": { - "version": "0.5.14", - "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.14.tgz", - "integrity": "sha512-2eczCSqBl1KUPJacZlFpQayvpilg3dxXLy9cSMTKtQMTQSmondUtPI47P3ikH3bQAXhzKLOE+qVxJ3/IRtu9pw==", - "dependencies": { - "@firebase/auth": "1.7.9", - "@firebase/auth-types": "0.12.2", - "@firebase/component": "0.6.9", - "@firebase/util": "1.10.0", - "tslib": "^2.1.0", - "undici": "6.19.7" - }, - "peerDependencies": { - "@firebase/app-compat": "0.x" - } - }, - "node_modules/@firebase/auth-interop-types": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.3.tgz", - "integrity": "sha512-Fc9wuJGgxoxQeavybiuwgyi+0rssr76b+nHpj+eGhXFYAdudMWyfBHvFL/I5fEHniUM/UQdFzi9VXJK2iZF7FQ==" - }, - "node_modules/@firebase/auth-types": { - "version": "0.12.2", - "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.12.2.tgz", - "integrity": "sha512-qsEBaRMoGvHO10unlDJhaKSuPn4pyoTtlQuP1ghZfzB6rNQPuhp/N/DcFZxm9i4v0SogjCbf9reWupwIvfmH6w==", - "peerDependencies": { - "@firebase/app-types": "0.x", - "@firebase/util": "1.x" - } - }, "node_modules/@firebase/component": { - "version": "0.6.9", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.9.tgz", - "integrity": "sha512-gm8EUEJE/fEac86AvHn8Z/QW8BvR56TBw3hMW0O838J/1mThYQXAIQBgUv75EqlCZfdawpWLrKt1uXvp9ciK3Q==", - "dependencies": { - "@firebase/util": "1.10.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@firebase/database": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.8.tgz", - "integrity": "sha512-dzXALZeBI1U5TXt6619cv0+tgEhJiwlUtQ55WNZY7vGAjv7Q1QioV969iYwt1AQQ0ovHnEW0YW9TiBfefLvErg==", - "dependencies": { - "@firebase/app-check-interop-types": "0.3.2", - "@firebase/auth-interop-types": "0.2.3", - "@firebase/component": "0.6.9", - "@firebase/logger": "0.4.2", - "@firebase/util": "1.10.0", - "faye-websocket": "0.11.4", - "tslib": "^2.1.0" - } - }, - "node_modules/@firebase/database-compat": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-1.0.8.tgz", - "integrity": "sha512-OpeWZoPE3sGIRPBKYnW9wLad25RaWbGyk7fFQe4xnJQKRzlynWeFBSRRAoLE2Old01WXwskUiucNqUUVlFsceg==", - "dependencies": { - "@firebase/component": "0.6.9", - "@firebase/database": "1.0.8", - "@firebase/database-types": "1.0.5", - "@firebase/logger": "0.4.2", - "@firebase/util": "1.10.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@firebase/database-types": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.5.tgz", - "integrity": "sha512-fTlqCNwFYyq/C6W7AJ5OCuq5CeZuBEsEwptnVxlNPkWCo5cTTyukzAHRSO/jaQcItz33FfYrrFk1SJofcu2AaQ==", - "dependencies": { - "@firebase/app-types": "0.9.2", - "@firebase/util": "1.10.0" - } - }, - "node_modules/@firebase/firestore": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.7.2.tgz", - "integrity": "sha512-WPkL/DEHuJg1PZPyHn81pNUhitG+7WkpLVdXmoYB23Za3eoM8VzuIn7zcD4Cji6wDCGA6eI1rvGYLtsXmE1OaQ==", + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.8.tgz", + "integrity": "sha512-LcNvxGLLGjBwB0dJUsBGCej2fqAepWyBubs4jt1Tiuns7QLbXHuyObZ4aMeBjZjWx4m8g1LoVI9QFpSaq/k4/g==", "dependencies": { - "@firebase/component": "0.6.9", - "@firebase/logger": "0.4.2", - "@firebase/util": "1.10.0", - "@firebase/webchannel-wrapper": "1.0.1", - "@grpc/grpc-js": "~1.9.0", - "@grpc/proto-loader": "^0.7.8", - "tslib": "^2.1.0", - "undici": "6.19.7" - }, - "engines": { - "node": ">=10.10.0" - }, - "peerDependencies": { - "@firebase/app": "0.x" - } - }, - "node_modules/@firebase/firestore-compat": { - "version": "0.3.37", - "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.37.tgz", - "integrity": "sha512-YwjJePx+m2OGnpKTGFTkcRXQZ+z0+8t7/zuwyOsTmKERobn0kekOv8VAQQmITcC+3du8Ul98O2a0vMH3xwt7jQ==", - "dependencies": { - "@firebase/component": "0.6.9", - "@firebase/firestore": "4.7.2", - "@firebase/firestore-types": "3.0.2", - "@firebase/util": "1.10.0", + "@firebase/util": "1.9.7", "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app-compat": "0.x" - } - }, - "node_modules/@firebase/firestore-types": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.2.tgz", - "integrity": "sha512-wp1A+t5rI2Qc/2q7r2ZpjUXkRVPtGMd6zCLsiWurjsQpqPgFin3AhNibKcIzoF2rnToNa/XYtyWXuifjOOwDgg==", - "peerDependencies": { - "@firebase/app-types": "0.x", - "@firebase/util": "1.x" } }, - "node_modules/@firebase/functions": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.11.8.tgz", - "integrity": "sha512-Lo2rTPDn96naFIlSZKVd1yvRRqqqwiJk7cf9TZhUerwnPKgBzXy+aHE22ry+6EjCaQusUoNai6mU6p+G8QZT1g==", - "dependencies": { - "@firebase/app-check-interop-types": "0.3.2", - "@firebase/auth-interop-types": "0.2.3", - "@firebase/component": "0.6.9", - "@firebase/messaging-interop-types": "0.2.2", - "@firebase/util": "1.10.0", - "tslib": "^2.1.0", - "undici": "6.19.7" - }, - "peerDependencies": { - "@firebase/app": "0.x" - } - }, - "node_modules/@firebase/functions-compat": { - "version": "0.3.14", - "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.14.tgz", - "integrity": "sha512-dZ0PKOKQFnOlMfcim39XzaXonSuPPAVuzpqA4ONTIdyaJK/OnBaIEVs/+BH4faa1a2tLeR+Jy15PKqDRQoNIJw==", - "dependencies": { - "@firebase/component": "0.6.9", - "@firebase/functions": "0.11.8", - "@firebase/functions-types": "0.6.2", - "@firebase/util": "1.10.0", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app-compat": "0.x" - } - }, - "node_modules/@firebase/functions-types": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.2.tgz", - "integrity": "sha512-0KiJ9lZ28nS2iJJvimpY4nNccV21rkQyor5Iheu/nq8aKXJqtJdeSlZDspjPSBBiHRzo7/GMUttegnsEITqR+w==" - }, "node_modules/@firebase/installations": { - "version": "0.6.9", - "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.9.tgz", - "integrity": "sha512-hlT7AwCiKghOX3XizLxXOsTFiFCQnp/oj86zp1UxwDGmyzsyoxtX+UIZyVyH/oBF5+XtblFG9KZzZQ/h+dpy+Q==", + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.8.tgz", + "integrity": "sha512-57V374qdb2+wT5v7+ntpLXBjZkO6WRgmAUbVkRfFTM/4t980p0FesbqTAcOIiM8U866UeuuuF8lYH70D3jM/jQ==", "dependencies": { - "@firebase/component": "0.6.9", - "@firebase/util": "1.10.0", + "@firebase/component": "0.6.8", + "@firebase/util": "1.9.7", "idb": "7.1.1", "tslib": "^2.1.0" }, @@ -5685,29 +5515,6 @@ "@firebase/app": "0.x" } }, - "node_modules/@firebase/installations-compat": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.9.tgz", - "integrity": "sha512-2lfdc6kPXR7WaL4FCQSQUhXcPbI7ol3wF+vkgtU25r77OxPf8F/VmswQ7sgIkBBWtymn5ZF20TIKtnOj9rjb6w==", - "dependencies": { - "@firebase/component": "0.6.9", - "@firebase/installations": "0.6.9", - "@firebase/installations-types": "0.5.2", - "@firebase/util": "1.10.0", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app-compat": "0.x" - } - }, - "node_modules/@firebase/installations-types": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.2.tgz", - "integrity": "sha512-que84TqGRZJpJKHBlF2pkvc1YcXrtEDOVGiDjovP/a3s6W4nlbohGXEsBJo0JCeeg/UG9A+DEZVDUV9GpklUzA==", - "peerDependencies": { - "@firebase/app-types": "0.x" - } - }, "node_modules/@firebase/logger": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.2.tgz", @@ -5716,183 +5523,29 @@ "tslib": "^2.1.0" } }, - "node_modules/@firebase/messaging": { - "version": "0.12.11", - "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.11.tgz", - "integrity": "sha512-zn5zGhF46BmiZ7W9yAUoHlqzJGakmWn1FNp//roXHN62dgdEFIKfXY7IODA2iQiXpmUO3sBdI/Tf+Hsft1mVkw==", - "dependencies": { - "@firebase/component": "0.6.9", - "@firebase/installations": "0.6.9", - "@firebase/messaging-interop-types": "0.2.2", - "@firebase/util": "1.10.0", - "idb": "7.1.1", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app": "0.x" - } - }, - "node_modules/@firebase/messaging-compat": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.11.tgz", - "integrity": "sha512-2NCkfE1L9jSn5OC+2n5rGAz5BEAQreK2lQGdPYQEJlAbKB2efoF+2FdiQ+LD8SlioSXz66REfeaEdesoLPFQcw==", - "dependencies": { - "@firebase/component": "0.6.9", - "@firebase/messaging": "0.12.11", - "@firebase/util": "1.10.0", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app-compat": "0.x" - } - }, - "node_modules/@firebase/messaging-interop-types": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.2.tgz", - "integrity": "sha512-l68HXbuD2PPzDUOFb3aG+nZj5KA3INcPwlocwLZOzPp9rFM9yeuI9YLl6DQfguTX5eAGxO0doTR+rDLDvQb5tA==" - }, "node_modules/@firebase/performance": { - "version": "0.6.9", - "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.6.9.tgz", - "integrity": "sha512-PnVaak5sqfz5ivhua+HserxTJHtCar/7zM0flCX6NkzBNzJzyzlH4Hs94h2Il0LQB99roBqoE5QT1JqWqcLJHQ==", - "dependencies": { - "@firebase/component": "0.6.9", - "@firebase/installations": "0.6.9", - "@firebase/logger": "0.4.2", - "@firebase/util": "1.10.0", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app": "0.x" - } - }, - "node_modules/@firebase/performance-compat": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.9.tgz", - "integrity": "sha512-dNl95IUnpsu3fAfYBZDCVhXNkASE0uo4HYaEPd2/PKscfTvsgqFAOxfAXzBEDOnynDWiaGUnb5M1O00JQ+3FXA==", + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.6.8.tgz", + "integrity": "sha512-F+alziiIZ6Yn8FG47mxwljq+4XkgkT2uJIFRlkyViUQRLzrogaUJW6u/+6ZrePXnouKlKIwzqos3PVJraPEcCA==", "dependencies": { - "@firebase/component": "0.6.9", + "@firebase/component": "0.6.8", + "@firebase/installations": "0.6.8", "@firebase/logger": "0.4.2", - "@firebase/performance": "0.6.9", - "@firebase/performance-types": "0.2.2", - "@firebase/util": "1.10.0", + "@firebase/util": "1.9.7", "tslib": "^2.1.0" }, - "peerDependencies": { - "@firebase/app-compat": "0.x" - } - }, - "node_modules/@firebase/performance-types": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.2.tgz", - "integrity": "sha512-gVq0/lAClVH5STrIdKnHnCo2UcPLjJlDUoEB/tB4KM+hAeHUxWKnpT0nemUPvxZ5nbdY/pybeyMe8Cs29gEcHA==" - }, - "node_modules/@firebase/remote-config": { - "version": "0.4.9", - "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.4.9.tgz", - "integrity": "sha512-EO1NLCWSPMHdDSRGwZ73kxEEcTopAxX1naqLJFNApp4hO8WfKfmEpmjxmP5TrrnypjIf2tUkYaKsfbEA7+AMmA==", - "dependencies": { - "@firebase/component": "0.6.9", - "@firebase/installations": "0.6.9", - "@firebase/logger": "0.4.2", - "@firebase/util": "1.10.0", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app": "0.x" - } - }, - "node_modules/@firebase/remote-config-compat": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.9.tgz", - "integrity": "sha512-AxzGpWfWFYejH2twxfdOJt5Cfh/ATHONegTd/a0p5flEzsD5JsxXgfkFToop+mypEL3gNwawxrxlZddmDoNxyA==", - "dependencies": { - "@firebase/component": "0.6.9", - "@firebase/logger": "0.4.2", - "@firebase/remote-config": "0.4.9", - "@firebase/remote-config-types": "0.3.2", - "@firebase/util": "1.10.0", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app-compat": "0.x" - } - }, - "node_modules/@firebase/remote-config-types": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.3.2.tgz", - "integrity": "sha512-0BC4+Ud7y2aPTyhXJTMTFfrGGLqdYXrUB9sJVAB8NiqJswDTc4/2qrE/yfUbnQJhbSi6ZaTTBKyG3n1nplssaA==" - }, - "node_modules/@firebase/storage": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.13.2.tgz", - "integrity": "sha512-fxuJnHshbhVwuJ4FuISLu+/76Aby2sh+44ztjF2ppoe0TELIDxPW6/r1KGlWYt//AD0IodDYYA8ZTN89q8YqUw==", - "dependencies": { - "@firebase/component": "0.6.9", - "@firebase/util": "1.10.0", - "tslib": "^2.1.0", - "undici": "6.19.7" - }, "peerDependencies": { "@firebase/app": "0.x" } }, - "node_modules/@firebase/storage-compat": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.12.tgz", - "integrity": "sha512-hA4VWKyGU5bWOll+uwzzhEMMYGu9PlKQc1w4DWxB3aIErWYzonrZjF0icqNQZbwKNIdh8SHjZlFeB2w6OSsjfg==", - "dependencies": { - "@firebase/component": "0.6.9", - "@firebase/storage": "0.13.2", - "@firebase/storage-types": "0.8.2", - "@firebase/util": "1.10.0", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app-compat": "0.x" - } - }, - "node_modules/@firebase/storage-types": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.2.tgz", - "integrity": "sha512-0vWu99rdey0g53lA7IShoA2Lol1jfnPovzLDUBuon65K7uKG9G+L5uO05brD9pMw+l4HRFw23ah3GwTGpEav6g==", - "peerDependencies": { - "@firebase/app-types": "0.x", - "@firebase/util": "1.x" - } - }, "node_modules/@firebase/util": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.10.0.tgz", - "integrity": "sha512-xKtx4A668icQqoANRxyDLBLz51TAbDP9KRfpbKGxiCAW346d0BeJe5vN6/hKxxmWwnZ0mautyv39JxviwwQMOQ==", + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.9.7.tgz", + "integrity": "sha512-fBVNH/8bRbYjqlbIhZ+lBtdAAS4WqZumx03K06/u7fJSpz1TGjEMm1ImvKD47w+xaFKIP2ori6z8BrbakRfjJA==", "dependencies": { "tslib": "^2.1.0" } }, - "node_modules/@firebase/vertexai-preview": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/@firebase/vertexai-preview/-/vertexai-preview-0.0.4.tgz", - "integrity": "sha512-EBSqyu9eg8frQlVU9/HjKtHN7odqbh9MtAcVz3WwHj4gLCLOoN9F/o+oxlq3CxvFrd3CNTZwu6d2mZtVlEInng==", - "dependencies": { - "@firebase/app-check-interop-types": "0.3.2", - "@firebase/component": "0.6.9", - "@firebase/logger": "0.4.2", - "@firebase/util": "1.10.0", - "tslib": "^2.1.0" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "@firebase/app": "0.x", - "@firebase/app-types": "0.x" - } - }, - "node_modules/@firebase/webchannel-wrapper": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.1.tgz", - "integrity": "sha512-jmEnr/pk0yVkA7mIlHNnxCi+wWzOFUg0WyIotgkKAb2u1J7fAeDBcVNSTjTihbAYNusCLQdW5s9IJ5qwnEufcQ==" - }, "node_modules/@formatjs/ecma402-abstract": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.0.0.tgz", @@ -6045,35 +5698,6 @@ "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, - "node_modules/@grpc/grpc-js": { - "version": "1.9.15", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.15.tgz", - "integrity": "sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ==", - "dependencies": { - "@grpc/proto-loader": "^0.7.8", - "@types/node": ">=12.12.47" - }, - "engines": { - "node": "^8.13.0 || >=10.10.0" - } - }, - "node_modules/@grpc/proto-loader": { - "version": "0.7.13", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", - "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", - "dependencies": { - "lodash.camelcase": "^4.3.0", - "long": "^5.0.0", - "protobufjs": "^7.2.5", - "yargs": "^17.7.2" - }, - "bin": { - "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/@hapi/hoek": { "version": "9.3.0", "license": "BSD-3-Clause" @@ -7818,60 +7442,6 @@ "integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==", "dev": true }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" - }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" - }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" - }, "node_modules/@radix-ui/primitive": { "version": "1.1.0", "dev": true, @@ -9524,61 +9094,44 @@ } }, "node_modules/@react-native-firebase/analytics": { - "version": "21.1.0", - "resolved": "https://registry.npmjs.org/@react-native-firebase/analytics/-/analytics-21.1.0.tgz", - "integrity": "sha512-geaZY4OGaxDW0j+5GVKHhFPOpbHDJxhobIgRVRDZLh6ycw56pA5dYA/uKcjf+JIb/OepHs1RVBr9tVKiryb4JA==", + "version": "12.9.3", + "license": "Apache-2.0", "peerDependencies": { - "@react-native-firebase/app": "21.1.0" + "@react-native-firebase/app": "12.9.3" } }, "node_modules/@react-native-firebase/app": { - "version": "21.1.0", - "resolved": "https://registry.npmjs.org/@react-native-firebase/app/-/app-21.1.0.tgz", - "integrity": "sha512-nb31yuAUqp39YD5YftdV6sOIjB9rk6J2tKpPiZfCsF70xkRxE+aQ1FhpmlGOorunqvK5Zrsf0rJzlbvLfgHIrQ==", + "version": "12.9.3", + "license": "Apache-2.0", "dependencies": { - "firebase": "10.13.2", + "@expo/config-plugins": "^4.0.3", + "opencollective-postinstall": "^2.0.1", "superstruct": "^0.6.2" }, "peerDependencies": { - "expo": ">=47.0.0", "react": "*", "react-native": "*" - }, - "peerDependenciesMeta": { - "expo": { - "optional": true - } } }, "node_modules/@react-native-firebase/crashlytics": { - "version": "21.1.0", - "resolved": "https://registry.npmjs.org/@react-native-firebase/crashlytics/-/crashlytics-21.1.0.tgz", - "integrity": "sha512-y/6so9qdlGBnpeY0BghD8afWZipOfdZ1LhnB1YlM6GVhBvo5qudorRyLKQQLZ2xoh1PGC7M+1IW9kw/2Qu4lVQ==", + "version": "12.9.3", + "license": "Apache-2.0", "dependencies": { - "stacktrace-js": "^2.0.2" + "@expo/config-plugins": "^4.0.3", + "stacktrace-js": "^2.0.0" }, "peerDependencies": { - "@react-native-firebase/app": "21.1.0", - "expo": ">=47.0.0" - }, - "peerDependenciesMeta": { - "expo": { - "optional": true - } + "@react-native-firebase/app": "12.9.3" } }, "node_modules/@react-native-firebase/perf": { - "version": "21.1.0", - "resolved": "https://registry.npmjs.org/@react-native-firebase/perf/-/perf-21.1.0.tgz", - "integrity": "sha512-u0W6RBPEOTKRzP55b7VQrF18S5YkyP/CFnuWXXoPZvNJTJDcH/68AH2Ih0a+tFths/3ZXDNzVhNE+03BV5uWqA==", - "peerDependencies": { - "@react-native-firebase/app": "21.1.0", - "expo": ">=47.0.0" + "version": "12.9.3", + "license": "Apache-2.0", + "dependencies": { + "@expo/config-plugins": "^4.0.3" }, - "peerDependenciesMeta": { - "expo": { - "optional": true - } + "peerDependencies": { + "@react-native-firebase/app": "12.9.3" } }, "node_modules/@react-native-google-signin/google-signin": { @@ -25122,6 +24675,7 @@ }, "node_modules/faye-websocket": { "version": "0.11.4", + "dev": true, "license": "Apache-2.0", "dependencies": { "websocket-driver": ">=0.5.1" @@ -25413,40 +24967,6 @@ "micromatch": "^4.0.2" } }, - "node_modules/firebase": { - "version": "10.13.2", - "resolved": "https://registry.npmjs.org/firebase/-/firebase-10.13.2.tgz", - "integrity": "sha512-YeI+TO5rJsoyZsVFx9WiN5ibdVCIigYTWwldRTMfCzrSPrJFVGao4acYj3x0EYGKDIgSgEyVBayD5BffD4Eyow==", - "dependencies": { - "@firebase/analytics": "0.10.8", - "@firebase/analytics-compat": "0.2.14", - "@firebase/app": "0.10.11", - "@firebase/app-check": "0.8.8", - "@firebase/app-check-compat": "0.3.15", - "@firebase/app-compat": "0.2.41", - "@firebase/app-types": "0.9.2", - "@firebase/auth": "1.7.9", - "@firebase/auth-compat": "0.5.14", - "@firebase/database": "1.0.8", - "@firebase/database-compat": "1.0.8", - "@firebase/firestore": "4.7.2", - "@firebase/firestore-compat": "0.3.37", - "@firebase/functions": "0.11.8", - "@firebase/functions-compat": "0.3.14", - "@firebase/installations": "0.6.9", - "@firebase/installations-compat": "0.2.9", - "@firebase/messaging": "0.12.11", - "@firebase/messaging-compat": "0.2.11", - "@firebase/performance": "0.6.9", - "@firebase/performance-compat": "0.2.9", - "@firebase/remote-config": "0.4.9", - "@firebase/remote-config-compat": "0.2.9", - "@firebase/storage": "0.13.2", - "@firebase/storage-compat": "0.3.12", - "@firebase/util": "1.10.0", - "@firebase/vertexai-preview": "0.0.4" - } - }, "node_modules/flat-cache": { "version": "3.0.4", "dev": true, @@ -26651,6 +26171,7 @@ }, "node_modules/http-parser-js": { "version": "0.5.8", + "dev": true, "license": "MIT" }, "node_modules/http-proxy": { @@ -30939,11 +30460,6 @@ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" }, - "node_modules/lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" - }, "node_modules/lodash.clonedeep": { "version": "4.5.0", "dev": true, @@ -31086,11 +30602,6 @@ "logkitty": "bin/logkitty.js" } }, - "node_modules/long": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", - "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" - }, "node_modules/loose-envify": { "version": "1.4.0", "license": "MIT", @@ -33214,6 +32725,13 @@ "undici-types": "~5.26.4" } }, + "node_modules/opencollective-postinstall": { + "version": "2.0.3", + "license": "MIT", + "bin": { + "opencollective-postinstall": "index.js" + } + }, "node_modules/opener": { "version": "1.5.2", "dev": true, @@ -34256,29 +33774,6 @@ "node": ">= 8" } }, - "node_modules/protobufjs": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", - "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", - "hasInstallScript": true, - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/node": ">=13.7.0", - "long": "^5.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/protocol-buffers-schema": { "version": "3.6.0", "license": "MIT" @@ -40646,14 +40141,6 @@ "version": "1.13.6", "license": "MIT" }, - "node_modules/undici": { - "version": "6.19.7", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.19.7.tgz", - "integrity": "sha512-HR3W/bMGPSr90i8AAp2C4DM3wChFdJPLrWYpIS++LxS8K+W535qftjt+4MyjNYHeWabMj1nvtmLIi7l++iq91A==", - "engines": { - "node": ">=18.17" - } - }, "node_modules/undici-types": { "version": "5.26.5", "license": "MIT" @@ -41877,6 +41364,7 @@ }, "node_modules/websocket-driver": { "version": "0.7.4", + "dev": true, "license": "Apache-2.0", "dependencies": { "http-parser-js": ">=0.5.1", @@ -41889,6 +41377,7 @@ }, "node_modules/websocket-extensions": { "version": "0.1.4", + "dev": true, "license": "Apache-2.0", "engines": { "node": ">=0.8.0" diff --git a/package.json b/package.json index d0cc6bb5b1f3..950b12b3a008 100644 --- a/package.json +++ b/package.json @@ -84,10 +84,10 @@ "@react-native-clipboard/clipboard": "^1.13.2", "@react-native-community/geolocation": "3.3.0", "@react-native-community/netinfo": "11.2.1", - "@react-native-firebase/analytics": "^21.1.0", - "@react-native-firebase/app": "^21.1.0", - "@react-native-firebase/crashlytics": "^21.1.0", - "@react-native-firebase/perf": "^21.1.0", + "@react-native-firebase/analytics": "^12.3.0", + "@react-native-firebase/app": "^12.3.0", + "@react-native-firebase/crashlytics": "^12.3.0", + "@react-native-firebase/perf": "^12.3.0", "@react-native-google-signin/google-signin": "^10.0.1", "@react-native-picker/picker": "2.7.6", "@react-navigation/material-top-tabs": "^6.6.3", @@ -378,4 +378,4 @@ "node": "20.15.1", "npm": "10.7.0" } -} \ No newline at end of file +} From 049dbfac718b53f8635ae002cc11e6fc06487b39 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Tue, 22 Oct 2024 16:29:03 -0400 Subject: [PATCH 062/438] Revert "update patches" This reverts commit 7d29053ede4da640f9bf6a0a2da86009f93027f4. --- ...t-native-firebase+analytics+12.9.3+001+hybrid-app.patch} | 6 +++--- ... @react-native-firebase+app+12.9.3+001+hybrid-app.patch} | 0 ...native-firebase+crashlytics+12.9.3+001+hybrid-app.patch} | 0 ...@react-native-firebase+perf+12.9.3+001+hybrid-app.patch} | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename patches/{@react-native-firebase+analytics+21.1.0+001+hybrid-app.patch => @react-native-firebase+analytics+12.9.3+001+hybrid-app.patch} (81%) rename patches/{@react-native-firebase+app+21.1.0+001+hybrid-app.patch => @react-native-firebase+app+12.9.3+001+hybrid-app.patch} (100%) rename patches/{@react-native-firebase+crashlytics+21.1.0+001+hybrid-app.patch => @react-native-firebase+crashlytics+12.9.3+001+hybrid-app.patch} (100%) rename patches/{@react-native-firebase+perf+21.1.0+001+hybrid-app.patch => @react-native-firebase+perf+12.9.3+001+hybrid-app.patch} (100%) diff --git a/patches/@react-native-firebase+analytics+21.1.0+001+hybrid-app.patch b/patches/@react-native-firebase+analytics+12.9.3+001+hybrid-app.patch similarity index 81% rename from patches/@react-native-firebase+analytics+21.1.0+001+hybrid-app.patch rename to patches/@react-native-firebase+analytics+12.9.3+001+hybrid-app.patch index 19464c3817d8..74d3e2a8005a 100644 --- a/patches/@react-native-firebase+analytics+21.1.0+001+hybrid-app.patch +++ b/patches/@react-native-firebase+analytics+12.9.3+001+hybrid-app.patch @@ -1,8 +1,8 @@ diff --git a/node_modules/@react-native-firebase/analytics/android/build.gradle b/node_modules/@react-native-firebase/analytics/android/build.gradle -index 5cd04f1..ab7ea07 100644 +index d223ebf..821b730 100644 --- a/node_modules/@react-native-firebase/analytics/android/build.gradle +++ b/node_modules/@react-native-firebase/analytics/android/build.gradle -@@ -44,6 +44,8 @@ if (coreVersionDetected != coreVersionRequired) { +@@ -45,6 +45,8 @@ if (coreVersionDetected != coreVersionRequired) { } } @@ -11,7 +11,7 @@ index 5cd04f1..ab7ea07 100644 project.ext { set('react-native', [ versions: [ -@@ -175,4 +177,3 @@ dependencies { +@@ -144,4 +146,3 @@ dependencies { ReactNative.shared.applyPackageVersion() ReactNative.shared.applyDefaultExcludes() ReactNative.module.applyAndroidVersions() diff --git a/patches/@react-native-firebase+app+21.1.0+001+hybrid-app.patch b/patches/@react-native-firebase+app+12.9.3+001+hybrid-app.patch similarity index 100% rename from patches/@react-native-firebase+app+21.1.0+001+hybrid-app.patch rename to patches/@react-native-firebase+app+12.9.3+001+hybrid-app.patch diff --git a/patches/@react-native-firebase+crashlytics+21.1.0+001+hybrid-app.patch b/patches/@react-native-firebase+crashlytics+12.9.3+001+hybrid-app.patch similarity index 100% rename from patches/@react-native-firebase+crashlytics+21.1.0+001+hybrid-app.patch rename to patches/@react-native-firebase+crashlytics+12.9.3+001+hybrid-app.patch diff --git a/patches/@react-native-firebase+perf+21.1.0+001+hybrid-app.patch b/patches/@react-native-firebase+perf+12.9.3+001+hybrid-app.patch similarity index 100% rename from patches/@react-native-firebase+perf+21.1.0+001+hybrid-app.patch rename to patches/@react-native-firebase+perf+12.9.3+001+hybrid-app.patch From 5fe33c2749f57e3875278ed827f02133fdd36f3e Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Tue, 22 Oct 2024 16:40:43 -0400 Subject: [PATCH 063/438] enable firebase debug mode in dev scheme --- .../xcshareddata/xcschemes/New Expensify Dev.xcscheme | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ios/NewExpensify.xcodeproj/xcshareddata/xcschemes/New Expensify Dev.xcscheme b/ios/NewExpensify.xcodeproj/xcshareddata/xcschemes/New Expensify Dev.xcscheme index 93d775217f11..9de4833e7274 100644 --- a/ios/NewExpensify.xcodeproj/xcshareddata/xcschemes/New Expensify Dev.xcscheme +++ b/ios/NewExpensify.xcodeproj/xcshareddata/xcschemes/New Expensify Dev.xcscheme @@ -60,6 +60,12 @@ ReferencedContainer = "container:NewExpensify.xcodeproj"> + + + + Date: Wed, 23 Oct 2024 14:26:58 +0700 Subject: [PATCH 064/438] refactor connection CONST --- src/CONST.ts | 36 +++++------- src/libs/actions/Report.ts | 112 ++++++++++++++++++------------------- 2 files changed, 70 insertions(+), 78 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 046577c5dd31..f657925a77c1 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -131,7 +131,7 @@ type OnboardingPurposeType = ValueOf; type OnboardingCompanySizeType = ValueOf; -type OnboardingAccountingType = ValueOf | null; +type OnboardingAccountingType = ValueOf | null; const onboardingInviteTypes = { IOU: 'iou', @@ -147,13 +147,6 @@ const onboardingCompanySize = { LARGE: '1001+', } as const; -const connectNames = { - QBO: 'quickbooksOnline', - XERO: 'xero', - NETSUITE: 'netsuite', - SAGE_INTACCT: 'intacct', -} as const; - type OnboardingInviteType = ValueOf; type OnboardingTaskType = { @@ -163,7 +156,7 @@ type OnboardingTaskType = { | string | (( params: Partial<{ - accountingName: string; + integrationName: string; }>, ) => string); description: @@ -174,8 +167,8 @@ type OnboardingTaskType = { workspaceCategoriesLink: string; workspaceMoreFeaturesLink: string; workspaceMembersLink: string; - accountingName: string; - accountingLink: string; + integrationName: string; + workspaceAccountingLink: string; }>, ) => string); }; @@ -4672,10 +4665,11 @@ const CONST = { "We'll send a request to each person so they can pay you back. Let me know if you have any questions!", }, ONBOARDING_ACCOUNTING_MAPPING: { - [connectNames.QBO]: 'QuickBooks Online', - [connectNames.XERO]: 'Xero', - [connectNames.NETSUITE]: 'NetSuite', - [connectNames.SAGE_INTACCT]: 'Sage Intacct', + quickbooksOnline: 'QuickBooks Online', + xero: 'Xero', + netsuite: 'NetSuite', + intacct: 'Sage Intacct', + quickbooksDesktop: 'QuickBooks Desktop', }, ONBOARDING_MESSAGES: { [onboardingChoices.EMPLOYER]: onboardingEmployerOrSubmitMessage, @@ -4791,20 +4785,20 @@ const CONST = { { type: 'addAccountingIntegration', autoCompleted: false, - title: ({accountingName}) => `Connect to ${accountingName}`, - description: ({accountingName, accountingLink}) => - `Connect to ${accountingName} for automatic expense coding and syncing that makes month-end close a breeze.\n` + + title: ({integrationName}) => `Connect to ${integrationName}`, + description: ({integrationName, workspaceAccountingLink}) => + `Connect to ${integrationName} for automatic expense coding and syncing that makes month-end close a breeze.\n` + '\n' + - `Here’s how to connect to ${accountingName}:\n` + + `Here’s how to connect to ${integrationName}:\n` + '\n' + '1. Click your profile photo.\n' + '2. Go to Workspaces.\n' + '3. Select your workspace.\n' + '4. Click Accounting.\n' + - `5. Find ${accountingName}.\n` + + `5. Find ${integrationName}.\n` + '6. Click Connect.\n' + '\n' + - `[Take me to Accounting!](${accountingLink})`, + `[Take me to Accounting!](${workspaceAccountingLink})`, }, ], }, diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 330c9b7c254a..67809d947430 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3330,7 +3330,7 @@ function completeOnboarding( companySize?: OnboardingCompanySizeType, userReportedIntegration?: OnboardingAccountingType, ) { - const accountingName = userReportedIntegration ? CONST.ONBOARDING_ACCOUNTING_MAPPING[userReportedIntegration] : ''; + const integrationName = userReportedIntegration ? CONST.ONBOARDING_ACCOUNTING_MAPPING[userReportedIntegration] : ''; const actorAccountID = CONST.ACCOUNT_ID.CONCIERGE; const targetChatReport = ReportUtils.getChatByParticipants([actorAccountID, currentUserAccountID]); const {reportID: targetChatReportID = '', policyID: targetChatPolicyID = ''} = targetChatReport ?? {}; @@ -3365,62 +3365,60 @@ function completeOnboarding( }; } - let tasksData = data.tasks.map((task, index) => { - const taskDescription = - typeof task.description === 'function' - ? task.description({ - adminsRoomLink: `${environmentURL}/${ROUTES.REPORT_WITH_ID.getRoute(adminsChatReportID ?? '-1')}`, - workspaceCategoriesLink: `${environmentURL}/${ROUTES.WORKSPACE_CATEGORIES.getRoute(onboardingPolicyID ?? '-1')}`, - workspaceMembersLink: `${environmentURL}/${ROUTES.WORKSPACE_MEMBERS.getRoute(onboardingPolicyID ?? '-1')}`, - workspaceMoreFeaturesLink: `${environmentURL}/${ROUTES.WORKSPACE_MORE_FEATURES.getRoute(onboardingPolicyID ?? '-1')}`, - accountingName, - accountingLink: `${environmentURL}/${ROUTES.POLICY_ACCOUNTING.getRoute(onboardingPolicyID ?? '-1')}`, - }) - : task.description; - const taskTitle = - typeof task.title === 'function' - ? task.title({ - accountingName, - }) - : task.title; - const currentTask = ReportUtils.buildOptimisticTaskReport( - actorAccountID, - currentUserAccountID, - targetChatReportID, - taskTitle, - taskDescription, - targetChatPolicyID, - CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, - ); - const taskCreatedAction = ReportUtils.buildOptimisticCreatedReportAction(CONST.EMAIL.CONCIERGE); - const taskReportAction = ReportUtils.buildOptimisticTaskCommentReportAction( - currentTask.reportID, - taskTitle, - 0, - `task for ${taskTitle}`, - targetChatReportID, - actorAccountID, - index + 3, - ); - currentTask.parentReportActionID = taskReportAction.reportAction.reportActionID; - - const completedTaskReportAction = task.autoCompleted - ? ReportUtils.buildOptimisticTaskReportAction(currentTask.reportID, CONST.REPORT.ACTIONS.TYPE.TASK_COMPLETED, 'marked as complete', actorAccountID, 2) - : null; - - return { - task, - currentTask, - taskCreatedAction, - taskReportAction, - taskDescription: currentTask.description, - completedTaskReportAction, - }; - }); - - if (!userReportedIntegration) { - tasksData = tasksData.filter((tasksDataItem) => tasksDataItem.task.type !== 'addAccountingIntegration'); - } + const tasksData = data.tasks + .filter((task) => !!userReportedIntegration || task.type !== 'addAccountingIntegration') + .map((task, index) => { + const taskDescription = + typeof task.description === 'function' + ? task.description({ + adminsRoomLink: `${environmentURL}/${ROUTES.REPORT_WITH_ID.getRoute(adminsChatReportID ?? '-1')}`, + workspaceCategoriesLink: `${environmentURL}/${ROUTES.WORKSPACE_CATEGORIES.getRoute(onboardingPolicyID ?? '-1')}`, + workspaceMembersLink: `${environmentURL}/${ROUTES.WORKSPACE_MEMBERS.getRoute(onboardingPolicyID ?? '-1')}`, + workspaceMoreFeaturesLink: `${environmentURL}/${ROUTES.WORKSPACE_MORE_FEATURES.getRoute(onboardingPolicyID ?? '-1')}`, + integrationName, + workspaceAccountingLink: `${environmentURL}/${ROUTES.POLICY_ACCOUNTING.getRoute(onboardingPolicyID ?? '-1')}`, + }) + : task.description; + const taskTitle = + typeof task.title === 'function' + ? task.title({ + integrationName, + }) + : task.title; + const currentTask = ReportUtils.buildOptimisticTaskReport( + actorAccountID, + currentUserAccountID, + targetChatReportID, + taskTitle, + taskDescription, + targetChatPolicyID, + CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, + ); + const taskCreatedAction = ReportUtils.buildOptimisticCreatedReportAction(CONST.EMAIL.CONCIERGE); + const taskReportAction = ReportUtils.buildOptimisticTaskCommentReportAction( + currentTask.reportID, + taskTitle, + 0, + `task for ${taskTitle}`, + targetChatReportID, + actorAccountID, + index + 3, + ); + currentTask.parentReportActionID = taskReportAction.reportAction.reportActionID; + + const completedTaskReportAction = task.autoCompleted + ? ReportUtils.buildOptimisticTaskReportAction(currentTask.reportID, CONST.REPORT.ACTIONS.TYPE.TASK_COMPLETED, 'marked as complete', actorAccountID, 2) + : null; + + return { + task, + currentTask, + taskCreatedAction, + taskReportAction, + taskDescription: currentTask.description, + completedTaskReportAction, + }; + }); const tasksForParameters = tasksData.map(({task, currentTask, taskCreatedAction, taskReportAction, taskDescription, completedTaskReportAction}) => ({ type: 'task', From 80fd4e774bb51f3aec476f6da5ae2a8f65bb8340 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Wed, 23 Oct 2024 10:50:28 +0200 Subject: [PATCH 065/438] add recentSearchHash --- src/components/Search/types.ts | 2 ++ src/libs/SearchQueryUtils.ts | 17 +++++++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts index 4f96090be9d0..43ebbed24943 100644 --- a/src/components/Search/types.ts +++ b/src/components/Search/types.ts @@ -76,6 +76,8 @@ type SearchQueryAST = { type SearchQueryJSON = { inputQuery: SearchQueryString; hash: number; + /** Hash used for putting queries in recent searches list. It ignores sortOrder and sortBy, because we want to treat queries differing only in sort params as the same query */ + recentSearchHash: number; flatFilters: QueryFilters; } & SearchQueryAST; diff --git a/src/libs/SearchQueryUtils.ts b/src/libs/SearchQueryUtils.ts index 32c2eff72007..7c7395ef1a9b 100644 --- a/src/libs/SearchQueryUtils.ts +++ b/src/libs/SearchQueryUtils.ts @@ -208,15 +208,13 @@ function findIDFromDisplayValue(filterName: ValueOf Date: Wed, 23 Oct 2024 11:40:38 +0200 Subject: [PATCH 066/438] Revert "Revert "[HybridApp] Allow classic experience users to use NewDot travel page"" This reverts commit 3d342ee74e418637451323a17cc54170aafbe659. --- src/ONYXKEYS.ts | 4 ++ src/components/InitialURLContextProvider.tsx | 7 +-- src/components/ScreenWrapper.tsx | 13 ++++- src/hooks/useOnboardingFlow.ts | 8 +-- src/libs/TripReservationUtils.ts | 25 +++++++-- src/libs/actions/Link.ts | 6 ++- src/libs/actions/Session/index.ts | 53 +++++++++++++------- tests/perf-test/ReportScreen.perf-test.tsx | 4 +- tests/perf-test/SearchRouter.perf-test.tsx | 2 + 9 files changed, 88 insertions(+), 34 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index d083a46d7760..14c0dc4abc50 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -441,6 +441,9 @@ const ONYXKEYS = { /** Stores recently used currencies */ RECENTLY_USED_CURRENCIES: 'nvp_recentlyUsedCurrencies', + /** States whether we transitioned from OldDot to show only certain group of screens. It should be undefined on pure NewDot. */ + IS_SINGLE_NEW_DOT_ENTRY: 'isSingleNewDotEntry', + /** Company cards custom names */ NVP_EXPENSIFY_COMPANY_CARDS_CUSTOM_NAMES: 'nvp_expensify_ccCustomNames', @@ -1004,6 +1007,7 @@ type OnyxValuesMapping = { [ONYXKEYS.APPROVAL_WORKFLOW]: OnyxTypes.ApprovalWorkflowOnyx; [ONYXKEYS.IMPORTED_SPREADSHEET]: OnyxTypes.ImportedSpreadsheet; [ONYXKEYS.LAST_ROUTE]: string; + [ONYXKEYS.IS_SINGLE_NEW_DOT_ENTRY]: boolean | undefined; [ONYXKEYS.IS_USING_IMPORTED_STATE]: boolean; [ONYXKEYS.SHOULD_SHOW_SAVED_SEARCH_RENAME_TOOLTIP]: boolean; [ONYXKEYS.NVP_EXPENSIFY_COMPANY_CARDS_CUSTOM_NAMES]: Record; diff --git a/src/components/InitialURLContextProvider.tsx b/src/components/InitialURLContextProvider.tsx index 85ad54ca6c94..f026f2de53f9 100644 --- a/src/components/InitialURLContextProvider.tsx +++ b/src/components/InitialURLContextProvider.tsx @@ -31,9 +31,10 @@ function InitialURLContextProvider({children, url}: InitialURLContextProviderPro useEffect(() => { if (url) { - const route = signInAfterTransitionFromOldDot(url); - setInitialURL(route); - setSplashScreenState(CONST.BOOT_SPLASH_STATE.READY_TO_BE_HIDDEN); + signInAfterTransitionFromOldDot(url).then((route) => { + setInitialURL(route); + setSplashScreenState(CONST.BOOT_SPLASH_STATE.READY_TO_BE_HIDDEN); + }); return; } Linking.getInitialURL().then((initURL) => { diff --git a/src/components/ScreenWrapper.tsx b/src/components/ScreenWrapper.tsx index 152a14fc3eb7..967d366aaa7c 100644 --- a/src/components/ScreenWrapper.tsx +++ b/src/components/ScreenWrapper.tsx @@ -1,9 +1,9 @@ -import {useIsFocused, useNavigation} from '@react-navigation/native'; +import {UNSTABLE_usePreventRemove, useIsFocused, useNavigation, useRoute} from '@react-navigation/native'; import type {StackNavigationProp} from '@react-navigation/stack'; import type {ForwardedRef, ReactNode} from 'react'; import React, {createContext, forwardRef, useEffect, useMemo, useRef, useState} from 'react'; import type {StyleProp, ViewStyle} from 'react-native'; -import {Keyboard, PanResponder, View} from 'react-native'; +import {Keyboard, NativeModules, PanResponder, View} from 'react-native'; import {PickerAvoidingView} from 'react-native-picker-select'; import type {EdgeInsets} from 'react-native-safe-area-context'; import useEnvironment from '@hooks/useEnvironment'; @@ -164,6 +164,15 @@ function ScreenWrapper( isKeyboardShownRef.current = keyboardState?.isKeyboardShown ?? false; + const route = useRoute(); + const shouldReturnToOldDot = useMemo(() => { + return !!route?.params && 'singleNewDotEntry' in route.params && route.params.singleNewDotEntry === 'true'; + }, [route]); + + UNSTABLE_usePreventRemove(shouldReturnToOldDot, () => { + NativeModules.HybridAppModule?.closeReactNativeApp(false, false); + }); + const panResponder = useRef( PanResponder.create({ onStartShouldSetPanResponderCapture: (_e, gestureState) => gestureState.numberActiveTouches === CONST.TEST_TOOL.NUMBER_OF_TAPS, diff --git a/src/hooks/useOnboardingFlow.ts b/src/hooks/useOnboardingFlow.ts index 5ccd3bab9378..9e97c552a6e0 100644 --- a/src/hooks/useOnboardingFlow.ts +++ b/src/hooks/useOnboardingFlow.ts @@ -21,14 +21,16 @@ function useOnboardingFlowRouter() { selector: hasCompletedHybridAppOnboardingFlowSelector, }); + const [isSingleNewDotEntry, isSingleNewDotEntryMetadata] = useOnyx(ONYXKEYS.IS_SINGLE_NEW_DOT_ENTRY); + useEffect(() => { - if (isLoadingOnyxValue(isOnboardingCompletedMetadata, isHybridAppOnboardingCompletedMetadata)) { + if (isLoadingOnyxValue(isOnboardingCompletedMetadata, isHybridAppOnboardingCompletedMetadata, isSingleNewDotEntryMetadata)) { return; } if (NativeModules.HybridAppModule) { // When user is transitioning from OldDot to NewDot, we usually show the explanation modal - if (isHybridAppOnboardingCompleted === false) { + if (isHybridAppOnboardingCompleted === false && !isSingleNewDotEntry) { Navigation.navigate(ROUTES.EXPLANATION_MODAL_ROOT); } @@ -43,7 +45,7 @@ function useOnboardingFlowRouter() { if (!NativeModules.HybridAppModule && isOnboardingCompleted === false) { OnboardingFlow.startOnboardingFlow(); } - }, [isOnboardingCompleted, isHybridAppOnboardingCompleted, isOnboardingCompletedMetadata, isHybridAppOnboardingCompletedMetadata]); + }, [isOnboardingCompleted, isHybridAppOnboardingCompleted, isOnboardingCompletedMetadata, isHybridAppOnboardingCompletedMetadata, isSingleNewDotEntryMetadata, isSingleNewDotEntry]); return {isOnboardingCompleted, isHybridAppOnboardingCompleted}; } diff --git a/src/libs/TripReservationUtils.ts b/src/libs/TripReservationUtils.ts index b7f754f9cac6..f2ce5113af81 100644 --- a/src/libs/TripReservationUtils.ts +++ b/src/libs/TripReservationUtils.ts @@ -1,5 +1,6 @@ import {Str} from 'expensify-common'; import type {Dispatch, SetStateAction} from 'react'; +import {NativeModules} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import type {LocaleContextProps} from '@components/LocaleContextProvider'; @@ -13,6 +14,7 @@ import type Transaction from '@src/types/onyx/Transaction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type IconAsset from '@src/types/utils/IconAsset'; import * as Link from './actions/Link'; +import Log from './Log'; import Navigation from './Navigation/Navigation'; import * as PolicyUtils from './PolicyUtils'; @@ -40,6 +42,14 @@ Onyx.connect({ }, }); +let isSingleNewDotEntry: boolean | undefined; +Onyx.connect({ + key: ONYXKEYS.IS_SINGLE_NEW_DOT_ENTRY, + callback: (val) => { + isSingleNewDotEntry = val; + }, +}); + function getTripReservationIcon(reservationType: ReservationType): IconAsset { switch (reservationType) { case CONST.RESERVATION_TYPE.FLIGHT: @@ -91,8 +101,17 @@ function bookATrip(translate: LocaleContextProps['translate'], setCtaErrorMessag if (ctaErrorMessage) { setCtaErrorMessage(''); } - Link.openTravelDotLink(activePolicyID)?.catch(() => { - setCtaErrorMessage(translate('travel.errorMessage')); - }); + Link.openTravelDotLink(activePolicyID) + ?.then(() => { + if (!NativeModules.HybridAppModule || !isSingleNewDotEntry) { + return; + } + + Log.info('[HybridApp] Returning to OldDot after opening TravelDot'); + NativeModules.HybridAppModule.closeReactNativeApp(false, false); + }) + ?.catch(() => { + setCtaErrorMessage(translate('travel.errorMessage')); + }); } export {getTripReservationIcon, getReservationsFromTripTransactions, getTripEReceiptIcon, bookATrip}; diff --git a/src/libs/actions/Link.ts b/src/libs/actions/Link.ts index 13fcea0df85d..4cda676d89e8 100644 --- a/src/libs/actions/Link.ts +++ b/src/libs/actions/Link.ts @@ -111,7 +111,7 @@ function openTravelDotLink(policyID: OnyxEntry, postLoginPath?: string) policyID, }; - return new Promise((_, reject) => { + return new Promise((resolve, reject) => { const error = new Error('Failed to generate spotnana token.'); asyncOpenURL( @@ -122,7 +122,9 @@ function openTravelDotLink(policyID: OnyxEntry, postLoginPath?: string) reject(error); throw error; } - return buildTravelDotURL(response.spotnanaToken, postLoginPath); + const travelURL = buildTravelDotURL(response.spotnanaToken, postLoginPath); + resolve(undefined); + return travelURL; }) .catch(() => { reject(error); diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index 37488442525d..d75c5064f93a 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -483,28 +483,43 @@ function signUpUser() { function signInAfterTransitionFromOldDot(transitionURL: string) { const [route, queryParams] = transitionURL.split('?'); - const {email, authToken, encryptedAuthToken, accountID, autoGeneratedLogin, autoGeneratedPassword, clearOnyxOnStart, completedHybridAppOnboarding} = Object.fromEntries( - queryParams.split('&').map((param) => { - const [key, value] = param.split('='); - return [key, value]; - }), - ); - - const setSessionDataAndOpenApp = () => { - Onyx.multiSet({ - [ONYXKEYS.SESSION]: {email, authToken, encryptedAuthToken: decodeURIComponent(encryptedAuthToken), accountID: Number(accountID)}, - [ONYXKEYS.CREDENTIALS]: {autoGeneratedLogin, autoGeneratedPassword}, - [ONYXKEYS.NVP_TRYNEWDOT]: {classicRedirect: {completedHybridAppOnboarding: completedHybridAppOnboarding === 'true'}}, - }).then(App.openApp); + const {email, authToken, encryptedAuthToken, accountID, autoGeneratedLogin, autoGeneratedPassword, clearOnyxOnStart, completedHybridAppOnboarding, isSingleNewDotEntry, primaryLogin} = + Object.fromEntries( + queryParams.split('&').map((param) => { + const [key, value] = param.split('='); + return [key, value]; + }), + ); + + const clearOnyxForNewAccount = () => { + if (clearOnyxOnStart !== 'true') { + return Promise.resolve(); + } + + return Onyx.clear(KEYS_TO_PRESERVE); }; - if (clearOnyxOnStart === 'true') { - Onyx.clear(KEYS_TO_PRESERVE).then(setSessionDataAndOpenApp); - } else { - setSessionDataAndOpenApp(); - } + const setSessionDataAndOpenApp = new Promise((resolve) => { + clearOnyxForNewAccount() + .then(() => + Onyx.multiSet({ + [ONYXKEYS.SESSION]: {email, authToken, encryptedAuthToken: decodeURIComponent(encryptedAuthToken), accountID: Number(accountID)}, + [ONYXKEYS.ACCOUNT]: {primaryLogin}, + [ONYXKEYS.CREDENTIALS]: {autoGeneratedLogin, autoGeneratedPassword}, + [ONYXKEYS.IS_SINGLE_NEW_DOT_ENTRY]: isSingleNewDotEntry === 'true', + [ONYXKEYS.NVP_TRYNEWDOT]: {classicRedirect: {completedHybridAppOnboarding: completedHybridAppOnboarding === 'true'}}, + }), + ) + .then(App.openApp) + .catch((error) => { + Log.hmmm('[HybridApp] Initialization of HybridApp has failed. Forcing transition', {error}); + }) + .finally(() => { + resolve(`${route}?singleNewDotEntry=${isSingleNewDotEntry}` as Route); + }); + }); - return route as Route; + return setSessionDataAndOpenApp; } /** diff --git a/tests/perf-test/ReportScreen.perf-test.tsx b/tests/perf-test/ReportScreen.perf-test.tsx index 550b6adabc36..b43004fcc82b 100644 --- a/tests/perf-test/ReportScreen.perf-test.tsx +++ b/tests/perf-test/ReportScreen.perf-test.tsx @@ -118,6 +118,8 @@ jest.mock('@react-navigation/native', () => { useFocusEffect: jest.fn(), useIsFocused: () => true, useRoute: () => jest.fn(), + // eslint-disable-next-line @typescript-eslint/naming-convention + UNSTABLE_usePreventRemove: () => jest.fn(), useNavigation: () => ({ navigate: jest.fn(), addListener: () => jest.fn(), @@ -231,7 +233,6 @@ test('[ReportScreen] should render ReportScreen', async () => { ...reportCollectionDataSet, ...reportActionsCollectionDataSet, }); - await measureRenders( { ...reportCollectionDataSet, ...reportActionsCollectionDataSet, }); - await measureRenders( { useFocusEffect: jest.fn(), useIsFocused: () => true, useRoute: () => jest.fn(), + // eslint-disable-next-line @typescript-eslint/naming-convention + UNSTABLE_usePreventRemove: () => jest.fn(), useNavigation: () => ({ navigate: jest.fn(), addListener: () => jest.fn(), From 9c866954d2f008884bcf862fa8e444e7ab8fd3c9 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Wed, 23 Oct 2024 12:28:44 +0200 Subject: [PATCH 067/438] fix: re-implement offline message logic --- src/pages/home/report/ReportActionsList.tsx | 45 +++++++++++++++------ 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index 80b046632784..4ec7517783a6 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -206,6 +206,9 @@ function ReportActionsList({ // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, [report.reportID]); + /** + * Whether a message is NOT from the active user and it was received while the user was offline. + */ const wasMessageReceivedWhileOffline = useCallback( (message: OnyxTypes.ReportAction) => !ReportActionsUtils.wasActionTakenByCurrentUser(message) && @@ -213,6 +216,23 @@ function ReportActionsList({ [isOffline, lastOfflineAt, lastOnlineAt, preferredLocale], ); + /** + * The index of the earliest message that was received while offline + */ + const earliestReceivedOfflineMessageIndex = useMemo(() => { + // Create a list of (sorted) indices of message that were received while offline + const receviedOfflineMessages = sortedReportActions.reduce((acc, message, index) => { + if (wasMessageReceivedWhileOffline(message)) { + acc[index] = index; + } + + return acc; + }, []); + + // The last index in the list is the earliest message that was received while offline + return receviedOfflineMessages.at(-1); + }, [sortedReportActions, wasMessageReceivedWhileOffline]); + /** * The reportActionID the unread marker should display above */ @@ -221,23 +241,24 @@ function ReportActionsList({ const isWithinVisibleThreshold = scrollingVerticalOffset.current < MSG_VISIBLE_THRESHOLD ? message.created < (userActiveSince.current ?? '') : true; const nextMessage = sortedVisibleReportActions.at(index + 1); - - // If the user recevied new messages while being offline, we want to display the unread marker above the first offline message. - const isCurrentMessageOffline = wasMessageReceivedWhileOffline(message); - const isNextMessageOffline = !!nextMessage && wasMessageReceivedWhileOffline(nextMessage); - - const isCurrentMessageUnread = ReportActionsUtils.isReportActionUnread(message, unreadMarkerTime); const isNextMessageUnread = !!nextMessage && ReportActionsUtils.isReportActionUnread(nextMessage, unreadMarkerTime); - const shouldDisplayForNextMessage = isNextMessageUnread || isNextMessageOffline; - const shouldDisplayBecauseOffline = isCurrentMessageOffline && !shouldDisplayForNextMessage; - const shouldDisplayBecauseUnread = isCurrentMessageUnread && !shouldDisplayForNextMessage && !ReportActionsUtils.shouldHideNewMarker(message) && isWithinVisibleThreshold; + // If the current message was received while offline, we want to display the unread marker above the earliest offline message. + const wasCurrentMessageReceivedWhileOffline = index === earliestReceivedOfflineMessageIndex; + if (wasCurrentMessageReceivedWhileOffline && !isNextMessageUnread) { + return true; + } - return shouldDisplayBecauseOffline || shouldDisplayBecauseUnread; + const isCurrentMessageUnread = ReportActionsUtils.isReportActionUnread(message, unreadMarkerTime); + return isCurrentMessageUnread && !isNextMessageUnread && !ReportActionsUtils.shouldHideNewMarker(message) && isWithinVisibleThreshold; }; + // If there are message that were recevied while offline, + // we can skip checking all messages later than the earliest recevied offline message. + const startIndex = earliestReceivedOfflineMessageIndex ?? 0; + // Scan through each visible report action until we find the appropriate action to show the unread marker - for (let index = 0; index < sortedVisibleReportActions.length; index++) { + for (let index = startIndex; index < sortedVisibleReportActions.length; index++) { const reportAction = sortedVisibleReportActions.at(index); if (reportAction && shouldDisplayNewMarker(reportAction, index)) { @@ -246,7 +267,7 @@ function ReportActionsList({ } return null; - }, [sortedVisibleReportActions, unreadMarkerTime, wasMessageReceivedWhileOffline]); + }, [earliestReceivedOfflineMessageIndex, sortedVisibleReportActions, unreadMarkerTime]); /** * Subscribe to read/unread events and update our unreadMarkerTime From a42aadcd277284a09fafa5638b73ae5d9b9276d3 Mon Sep 17 00:00:00 2001 From: Mateusz Rajski Date: Wed, 23 Oct 2024 13:14:42 +0200 Subject: [PATCH 068/438] Fix bug when user cannot return from Travel page --- src/libs/Navigation/NavigationRoot.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/Navigation/NavigationRoot.tsx b/src/libs/Navigation/NavigationRoot.tsx index bb005fc6b763..c23c3783b3bf 100644 --- a/src/libs/Navigation/NavigationRoot.tsx +++ b/src/libs/Navigation/NavigationRoot.tsx @@ -108,7 +108,8 @@ function NavigationRoot({authenticated, lastVisitedPath, initialUrl, onReady, sh } // If there is no lastVisitedPath, we can do early return. We won't modify the default behavior. - if (!lastVisitedPath) { + // The same applies to HybridApp, as we always define the route to which we want to transition. + if (!lastVisitedPath || NativeModules.HybridAppModule) { return undefined; } From 5e98efc6a8f25ddc9f4113902881e829782b8a2a Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Wed, 23 Oct 2024 13:16:33 +0200 Subject: [PATCH 069/438] integrate press selection handler for web TextInput --- src/components/AmountForm.tsx | 4 +- src/components/MoneyRequestAmountInput.tsx | 8 ++-- .../BaseTextInputWithCurrencySymbol.tsx | 4 +- .../index.android.tsx | 4 +- .../TextInputWithCurrencySymbol/index.tsx | 8 +++- .../TextInputWithCurrencySymbol/index.web.tsx | 42 +++++++++++++++++++ .../TextInputWithCurrencySymbol/types.ts | 12 ++++-- 7 files changed, 67 insertions(+), 15 deletions(-) create mode 100644 src/components/TextInputWithCurrencySymbol/index.web.tsx diff --git a/src/components/AmountForm.tsx b/src/components/AmountForm.tsx index 4848577bdea0..aa2ff1387de4 100644 --- a/src/components/AmountForm.tsx +++ b/src/components/AmountForm.tsx @@ -16,7 +16,7 @@ import TextInput from './TextInput'; import isTextInputFocused from './TextInput/BaseTextInput/isTextInputFocused'; import type {BaseTextInputProps, BaseTextInputRef} from './TextInput/BaseTextInput/types'; import TextInputWithCurrencySymbol from './TextInputWithCurrencySymbol'; -import type TextInputWithCurrencySymbolProps from './TextInputWithCurrencySymbol/types'; +import type BaseTextInputWithCurrencySymbolProps from './TextInputWithCurrencySymbol/types'; type AmountFormProps = { /** Amount supplied by the FormProvider */ @@ -51,7 +51,7 @@ type AmountFormProps = { /** Number of decimals to display */ fixedDecimals?: number; -} & Pick & +} & Pick & Pick; /** diff --git a/src/components/MoneyRequestAmountInput.tsx b/src/components/MoneyRequestAmountInput.tsx index 702e6c384b58..f83914c0ee51 100644 --- a/src/components/MoneyRequestAmountInput.tsx +++ b/src/components/MoneyRequestAmountInput.tsx @@ -1,6 +1,6 @@ import type {ForwardedRef} from 'react'; import React, {useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; -import type {NativeSyntheticEvent, StyleProp, TextInputSelectionChangeEventData, TextStyle, ViewStyle} from 'react-native'; +import type {NativeSyntheticEvent, StyleProp, TextStyle, ViewStyle} from 'react-native'; import useLocalize from '@hooks/useLocalize'; import {useMouseContext} from '@hooks/useMouseContext'; import * as Browser from '@libs/Browser'; @@ -304,7 +304,7 @@ function MoneyRequestAmountInput( }} selectedCurrencyCode={currency} selection={selection} - onSelectionChange={(e: NativeSyntheticEvent) => { + onSelectionChange={(selectionStart, selectionEnd) => { if (shouldIgnoreSelectionWhenUpdatedManually && willSelectionBeUpdatedManually.current) { willSelectionBeUpdatedManually.current = false; return; @@ -313,8 +313,8 @@ function MoneyRequestAmountInput( return; } const maxSelection = formattedAmount.length; - const start = Math.min(e.nativeEvent.selection.start, maxSelection); - const end = Math.min(e.nativeEvent.selection.end, maxSelection); + const start = Math.min(selectionStart, maxSelection); + const end = Math.min(selectionEnd, maxSelection); setSelection({start, end}); }} onKeyPress={textInputKeyPress} diff --git a/src/components/TextInputWithCurrencySymbol/BaseTextInputWithCurrencySymbol.tsx b/src/components/TextInputWithCurrencySymbol/BaseTextInputWithCurrencySymbol.tsx index 3e7c5f0bc414..4c30b048c1af 100644 --- a/src/components/TextInputWithCurrencySymbol/BaseTextInputWithCurrencySymbol.tsx +++ b/src/components/TextInputWithCurrencySymbol/BaseTextInputWithCurrencySymbol.tsx @@ -7,7 +7,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import * as MoneyRequestUtils from '@libs/MoneyRequestUtils'; import type {BaseTextInputRef} from '@src/components/TextInput/BaseTextInput/types'; -import type TextInputWithCurrencySymbolProps from './types'; +import type BaseTextInputWithCurrencySymbolProps from './types'; function BaseTextInputWithCurrencySymbol( { @@ -24,7 +24,7 @@ function BaseTextInputWithCurrencySymbol( extraSymbol, style, ...rest - }: TextInputWithCurrencySymbolProps, + }: BaseTextInputWithCurrencySymbolProps, ref: React.ForwardedRef, ) { const {fromLocaleDigit} = useLocalize(); diff --git a/src/components/TextInputWithCurrencySymbol/index.android.tsx b/src/components/TextInputWithCurrencySymbol/index.android.tsx index cbd822d07cf8..f7794c5822ff 100644 --- a/src/components/TextInputWithCurrencySymbol/index.android.tsx +++ b/src/components/TextInputWithCurrencySymbol/index.android.tsx @@ -2,7 +2,7 @@ import React, {useEffect, useState} from 'react'; import type {NativeSyntheticEvent, TextInputSelectionChangeEventData} from 'react-native'; import type {BaseTextInputRef} from '@src/components/TextInput/BaseTextInput/types'; import BaseTextInputWithCurrencySymbol from './BaseTextInputWithCurrencySymbol'; -import type TextInputWithCurrencySymbolProps from './types'; +import type {TextInputWithCurrencySymbolProps} from './types'; function TextInputWithCurrencySymbol({onSelectionChange = () => {}, ...props}: TextInputWithCurrencySymbolProps, ref: React.ForwardedRef) { const [skipNextSelectionChange, setSkipNextSelectionChange] = useState(false); @@ -21,7 +21,7 @@ function TextInputWithCurrencySymbol({onSelectionChange = () => {}, ...props}: T setSkipNextSelectionChange(false); return; } - onSelectionChange(event); + onSelectionChange(event.nativeEvent.selection.start, event.nativeEvent.selection.end); }} /> ); diff --git a/src/components/TextInputWithCurrencySymbol/index.tsx b/src/components/TextInputWithCurrencySymbol/index.tsx index b6230061bb6c..9e8333d9db23 100644 --- a/src/components/TextInputWithCurrencySymbol/index.tsx +++ b/src/components/TextInputWithCurrencySymbol/index.tsx @@ -1,14 +1,18 @@ import React from 'react'; +import type {NativeSyntheticEvent, TextInputSelectionChangeEventData} from 'react-native'; import type {BaseTextInputRef} from '@src/components/TextInput/BaseTextInput/types'; import BaseTextInputWithCurrencySymbol from './BaseTextInputWithCurrencySymbol'; -import type TextInputWithCurrencySymbolProps from './types'; +import type {TextInputWithCurrencySymbolProps} from './types'; -function TextInputWithCurrencySymbol(props: TextInputWithCurrencySymbolProps, ref: React.ForwardedRef) { +function TextInputWithCurrencySymbol({onSelectionChange = () => {}, ...props}: TextInputWithCurrencySymbolProps, ref: React.ForwardedRef) { return ( ) => { + onSelectionChange(event.nativeEvent.selection.start, event.nativeEvent.selection.end); + }} /> ); } diff --git a/src/components/TextInputWithCurrencySymbol/index.web.tsx b/src/components/TextInputWithCurrencySymbol/index.web.tsx new file mode 100644 index 000000000000..f86b764f79cc --- /dev/null +++ b/src/components/TextInputWithCurrencySymbol/index.web.tsx @@ -0,0 +1,42 @@ +import React, {useRef} from 'react'; +import type {NativeSyntheticEvent, TextInputSelectionChangeEventData} from 'react-native'; +import BaseTextInputWithCurrencySymbol from './BaseTextInputWithCurrencySymbol'; +import type {TextInputWithCurrencySymbolProps} from './types'; + +function TextInputWithCurrencySymbol({onSelectionChange = () => {}, ...props}: TextInputWithCurrencySymbolProps, ref: React.ForwardedRef) { + const textInputRef = useRef(null); + + return ( + { + textInputRef.current = element as HTMLFormElement; + + if (!ref) { + return; + } + + if (typeof ref === 'function') { + ref(element as HTMLFormElement); + return; + } + + // eslint-disable-next-line no-param-reassign + ref.current = element as HTMLFormElement; + }} + onSelectionChange={(event: NativeSyntheticEvent) => { + onSelectionChange(event.nativeEvent.selection.start, event.nativeEvent.selection.end); + }} + onPress={() => { + const selectionStart = (textInputRef.current?.selectionStart as number) ?? 0; + const selectionEnd = (textInputRef.current?.selectionEnd as number) ?? 0; + onSelectionChange(selectionStart, selectionEnd); + }} + /> + ); +} + +TextInputWithCurrencySymbol.displayName = 'TextInputWithCurrencySymbol'; + +export default React.forwardRef(TextInputWithCurrencySymbol); diff --git a/src/components/TextInputWithCurrencySymbol/types.ts b/src/components/TextInputWithCurrencySymbol/types.ts index 619ed0fd84e6..401af75b16cd 100644 --- a/src/components/TextInputWithCurrencySymbol/types.ts +++ b/src/components/TextInputWithCurrencySymbol/types.ts @@ -2,7 +2,7 @@ import type {NativeSyntheticEvent, StyleProp, TextInputFocusEventData, TextInput import type {TextSelection} from '@components/Composer/types'; import type {BaseTextInputProps} from '@components/TextInput/BaseTextInput/types'; -type TextInputWithCurrencySymbolProps = { +type BaseTextInputWithCurrencySymbolProps = { /** Formatted amount in local currency */ formattedAmount: string; @@ -77,6 +77,12 @@ type TextInputWithCurrencySymbolProps = { /** Hide the focus styles on TextInput */ hideFocusedState?: boolean; -} & Pick; +} & Pick; -export default TextInputWithCurrencySymbolProps; +type TextInputWithCurrencySymbolProps = Omit & { + onSelectionChange?: (start: number, end: number) => void; +}; + +export type {TextInputWithCurrencySymbolProps}; + +export default BaseTextInputWithCurrencySymbolProps; From 045b59aa67e4d8aea9d7518b6ce2cdcda1b8511c Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Wed, 23 Oct 2024 13:18:29 +0200 Subject: [PATCH 070/438] integrate changes to AmountForm --- src/components/AmountForm.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/AmountForm.tsx b/src/components/AmountForm.tsx index aa2ff1387de4..a230dfa1af8d 100644 --- a/src/components/AmountForm.tsx +++ b/src/components/AmountForm.tsx @@ -1,6 +1,6 @@ import type {ForwardedRef} from 'react'; import React, {forwardRef, useCallback, useEffect, useMemo, useRef, useState} from 'react'; -import type {NativeSyntheticEvent, TextInputSelectionChangeEventData} from 'react-native'; +import type {NativeSyntheticEvent} from 'react-native'; import {View} from 'react-native'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -290,11 +290,11 @@ function AmountForm( }} selectedCurrencyCode={currency} selection={selection} - onSelectionChange={(e: NativeSyntheticEvent) => { + onSelectionChange={(start, end) => { if (!shouldUpdateSelection) { return; } - setSelection(e.nativeEvent.selection); + setSelection({start, end}); }} onKeyPress={textInputKeyPress} isCurrencyPressable={isCurrencyPressable} From 92c54422ab76eb9c318d488175b7f031c5b5a4c5 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Wed, 23 Oct 2024 11:39:52 -0400 Subject: [PATCH 071/438] use googleService-Info.plist from firebase --- ios/GoogleService-Info.plist | 2 ++ ios/NewExpensify.xcodeproj/project.pbxproj | 18 +++++++++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/ios/GoogleService-Info.plist b/ios/GoogleService-Info.plist index 147bec8c2875..151e3c0cf8aa 100644 --- a/ios/GoogleService-Info.plist +++ b/ios/GoogleService-Info.plist @@ -6,6 +6,8 @@ 921154746561-s3uqn2oe4m85tufi6mqflbfbuajrm2i3.apps.googleusercontent.com REVERSED_CLIENT_ID com.googleusercontent.apps.921154746561-s3uqn2oe4m85tufi6mqflbfbuajrm2i3 + ANDROID_CLIENT_ID + 921154746561-cbegir0tnc2gan6k1gre5vtn75p60hom.apps.googleusercontent.com API_KEY AIzaSyA9Qn7q5Iw26gTzjI7012C4PaFrFagpC_I GCM_SENDER_ID diff --git a/ios/NewExpensify.xcodeproj/project.pbxproj b/ios/NewExpensify.xcodeproj/project.pbxproj index 96baba0d4e87..f1d9896c209e 100644 --- a/ios/NewExpensify.xcodeproj/project.pbxproj +++ b/ios/NewExpensify.xcodeproj/project.pbxproj @@ -18,7 +18,6 @@ 0CDA8E38287DD6A0004ECBEC /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0CDA8E36287DD6A0004ECBEC /* Images.xcassets */; }; 0DFC45942C884E0A00B56C91 /* RCTShortcutManagerModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DFC45932C884E0A00B56C91 /* RCTShortcutManagerModule.m */; }; 0DFC45952C884E0A00B56C91 /* RCTShortcutManagerModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DFC45932C884E0A00B56C91 /* RCTShortcutManagerModule.m */; }; - 0F5BE0CE252686330097D869 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0F5BE0CD252686320097D869 /* GoogleService-Info.plist */; }; 0F5E5350263B73FD004CA14F /* EnvironmentChecker.m in Sources */ = {isa = PBXBuildFile; fileRef = 0F5E534F263B73FD004CA14F /* EnvironmentChecker.m */; }; 0F5E5351263B73FD004CA14F /* EnvironmentChecker.m in Sources */ = {isa = PBXBuildFile; fileRef = 0F5E534F263B73FD004CA14F /* EnvironmentChecker.m */; }; 1246A3EF20E54E7A9494C8B9 /* ExpensifyNeue-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = F4F8A052A22040339996324B /* ExpensifyNeue-Regular.otf */; }; @@ -34,6 +33,9 @@ 70CF6E82262E297300711ADC /* BootSplash.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 70CF6E81262E297300711ADC /* BootSplash.storyboard */; }; 7F5E81F06BCCF61AD02CEA06 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCD444BEDDB0AF1745B39049 /* ExpoModulesProvider.swift */; }; 7F9DD8DA2B2A445B005E3AFA /* ExpError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F9DD8D92B2A445B005E3AFA /* ExpError.swift */; }; + 7FB680AE2CC94EDA006693CF /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7FB680AD2CC94EDA006693CF /* GoogleService-Info.plist */; }; + 7FB680AF2CC94EDA006693CF /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7FB680AD2CC94EDA006693CF /* GoogleService-Info.plist */; }; + 7FB680B02CC94EDA006693CF /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7FB680AD2CC94EDA006693CF /* GoogleService-Info.plist */; }; 7FD73C9E2B23CE9500420AF3 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FD73C9D2B23CE9500420AF3 /* NotificationService.swift */; }; 7FD73CA22B23CE9500420AF3 /* NotificationServiceExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 7FD73C9B2B23CE9500420AF3 /* NotificationServiceExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 8744C5400E24E379441C04A4 /* libPods-NewExpensify.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 59A21B2405370FDDD847C813 /* libPods-NewExpensify.a */; }; @@ -43,7 +45,7 @@ D27CE6B77196EF3EF450EEAC /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 0D3F9E814828D91464DF9D35 /* PrivacyInfo.xcprivacy */; }; DD79042B2792E76D004484B4 /* RCTBootSplash.mm in Sources */ = {isa = PBXBuildFile; fileRef = DD79042A2792E76D004484B4 /* RCTBootSplash.mm */; }; DDCB2E57F334C143AC462B43 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D20D83B0E39BA6D21761E72 /* ExpoModulesProvider.swift */; }; - E51DC681C7DEE40AEBDDFBFE /* BuildFile in Frameworks */ = {isa = PBXBuildFile; }; + E51DC681C7DEE40AEBDDFBFE /* (null) in Frameworks */ = {isa = PBXBuildFile; }; E9DF872D2525201700607FDC /* AirshipConfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = E9DF872C2525201700607FDC /* AirshipConfig.plist */; }; ED222ED90E074A5481A854FA /* ExpensifyNeue-BoldItalic.otf in Resources */ = {isa = PBXBuildFile; fileRef = 8B28D84EF339436DBD42A203 /* ExpensifyNeue-BoldItalic.otf */; }; F0C450EA2705020500FD2970 /* colors.json in Resources */ = {isa = PBXBuildFile; fileRef = F0C450E92705020500FD2970 /* colors.json */; }; @@ -94,7 +96,6 @@ 0D3F9E814828D91464DF9D35 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = NewExpensify/PrivacyInfo.xcprivacy; sourceTree = ""; }; 0DFC45922C884D7900B56C91 /* RCTShortcutManagerModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTShortcutManagerModule.h; sourceTree = ""; }; 0DFC45932C884E0A00B56C91 /* RCTShortcutManagerModule.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTShortcutManagerModule.m; sourceTree = ""; }; - 0F5BE0CD252686320097D869 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 0F5E534E263B73D5004CA14F /* EnvironmentChecker.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EnvironmentChecker.h; sourceTree = ""; }; 0F5E534F263B73FD004CA14F /* EnvironmentChecker.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EnvironmentChecker.m; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* New Expensify Dev.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "New Expensify Dev.app"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -133,6 +134,7 @@ 7F3784A72C75131000063508 /* NewExpensifyReleaseProduction.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = NewExpensifyReleaseProduction.entitlements; path = NewExpensify/NewExpensifyReleaseProduction.entitlements; sourceTree = ""; }; 7F9C91352CA5EC4900FC4DC1 /* NotificationServiceExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NotificationServiceExtension.entitlements; sourceTree = ""; }; 7F9DD8D92B2A445B005E3AFA /* ExpError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpError.swift; sourceTree = ""; }; + 7FB680AD2CC94EDA006693CF /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 7FD73C9B2B23CE9500420AF3 /* NotificationServiceExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotificationServiceExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 7FD73C9D2B23CE9500420AF3 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; 7FD73C9F2B23CE9500420AF3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -176,8 +178,8 @@ buildActionMask = 2147483647; files = ( 383643682B6D4AE2005BB9AE /* DeviceCheck.framework in Frameworks */, - E51DC681C7DEE40AEBDDFBFE /* BuildFile in Frameworks */, - E51DC681C7DEE40AEBDDFBFE /* BuildFile in Frameworks */, + E51DC681C7DEE40AEBDDFBFE /* (null) in Frameworks */, + E51DC681C7DEE40AEBDDFBFE /* (null) in Frameworks */, 8744C5400E24E379441C04A4 /* libPods-NewExpensify.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -212,13 +214,13 @@ 13B07FAE1A68108700A75B9A /* NewExpensify */ = { isa = PBXGroup; children = ( + 7FB680AD2CC94EDA006693CF /* GoogleService-Info.plist */, 7F3784A72C75131000063508 /* NewExpensifyReleaseProduction.entitlements */, 7F3784A62C7512D900063508 /* NewExpensifyReleaseAdHoc.entitlements */, 7F3784A52C7512CF00063508 /* NewExpensifyReleaseDevelopment.entitlements */, 7F3784A42C7512BF00063508 /* NewExpensifyDebugProduction.entitlements */, 7F3784A32C75129D00063508 /* NewExpensifyDebugAdHoc.entitlements */, 7F3784A22C75103800063508 /* NewExpensifyDebugDevelopment.entitlements */, - 0F5BE0CD252686320097D869 /* GoogleService-Info.plist */, E9DF872C2525201700607FDC /* AirshipConfig.plist */, 0CDA8E36287DD6A0004ECBEC /* Images.xcassets */, 008F07F21AC5B25A0029DE68 /* main.jsbundle */, @@ -500,6 +502,7 @@ buildActionMask = 2147483647; files = ( 0CDA8E38287DD6A0004ECBEC /* Images.xcassets in Resources */, + 7FB680B02CC94EDA006693CF /* GoogleService-Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -507,7 +510,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 0F5BE0CE252686330097D869 /* GoogleService-Info.plist in Resources */, E9DF872D2525201700607FDC /* AirshipConfig.plist in Resources */, F0C450EA2705020500FD2970 /* colors.json in Resources */, 083353EB2B5AB22A00C603C0 /* attention.mp3 in Resources */, @@ -519,6 +521,7 @@ 083353EE2B5AB22A00C603C0 /* success.mp3 in Resources */, 0C7C65547D7346EB923BE808 /* ExpensifyMono-Regular.otf in Resources */, 2A9F8CDA983746B0B9204209 /* ExpensifyNeue-Bold.otf in Resources */, + 7FB680AE2CC94EDA006693CF /* GoogleService-Info.plist in Resources */, 083353EC2B5AB22A00C603C0 /* done.mp3 in Resources */, 083353ED2B5AB22A00C603C0 /* receive.mp3 in Resources */, ED222ED90E074A5481A854FA /* ExpensifyNeue-BoldItalic.otf in Resources */, @@ -532,6 +535,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 7FB680AF2CC94EDA006693CF /* GoogleService-Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; From 673b880fd4245682529d912cf79a162bcac2ea1b Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 23 Oct 2024 19:04:18 +0300 Subject: [PATCH 072/438] added button alternative to free trial component --- .../LHNOptionsList/OptionRowLHN.tsx | 4 ++-- src/pages/home/HeaderView.tsx | 4 ++-- .../settings/Subscription/FreeTrialBadge.tsx | 20 +++++++++++++++---- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/components/LHNOptionsList/OptionRowLHN.tsx b/src/components/LHNOptionsList/OptionRowLHN.tsx index bd19b3a032ea..6271a7b29f42 100644 --- a/src/components/LHNOptionsList/OptionRowLHN.tsx +++ b/src/components/LHNOptionsList/OptionRowLHN.tsx @@ -28,7 +28,7 @@ import Performance from '@libs/Performance'; import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager'; import * as ReportUtils from '@libs/ReportUtils'; import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportActionContextMenu'; -import FreeTrialBadge from '@pages/settings/Subscription/FreeTrialBadge'; +import FreeTrial from '@pages/settings/Subscription/FreeTrialBadge'; import variables from '@styles/variables'; import * as User from '@userActions/User'; import CONST from '@src/CONST'; @@ -277,7 +277,7 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti ReportUtils.isSystemChat(report) } /> - {ReportUtils.isChatUsedForOnboarding(report) && } + {ReportUtils.isChatUsedForOnboarding(report) && } {isStatusVisible && ( - {ReportUtils.isChatUsedForOnboarding(report) && } + {ReportUtils.isChatUsedForOnboarding(report) && } {isTaskReport && !shouldUseNarrowLayout && ReportUtils.isOpenTaskReport(report, parentReportAction) && } {canJoin && !shouldUseNarrowLayout && joinButton} diff --git a/src/pages/settings/Subscription/FreeTrialBadge.tsx b/src/pages/settings/Subscription/FreeTrialBadge.tsx index 5379d5a888b7..e250a1d7ad19 100644 --- a/src/pages/settings/Subscription/FreeTrialBadge.tsx +++ b/src/pages/settings/Subscription/FreeTrialBadge.tsx @@ -2,15 +2,20 @@ import React, {useEffect, useState} from 'react'; import type {StyleProp, ViewStyle} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import Badge from '@components/Badge'; +import Button from '@components/Button'; import useNetwork from '@hooks/useNetwork'; +import Navigation from '@libs/Navigation/Navigation'; import * as SubscriptionUtils from '@libs/SubscriptionUtils'; +import * as Expensicons from '@src/components/Icon/Expensicons'; import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; type FreeTrialBadgeProps = { badgeStyles?: StyleProp; + pressable?: boolean; }; -function FreeTrialBadge({badgeStyles}: FreeTrialBadgeProps) { +function FreeTrial({badgeStyles, pressable = false}: FreeTrialBadgeProps) { const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); const [firstDayFreeTrial] = useOnyx(ONYXKEYS.NVP_FIRST_DAY_FREE_TRIAL); const [lastDayFreeTrial] = useOnyx(ONYXKEYS.NVP_LAST_DAY_FREE_TRIAL); @@ -30,7 +35,14 @@ function FreeTrialBadge({badgeStyles}: FreeTrialBadgeProps) { return null; } - return ( + return pressable ? ( +