From 86aa3a106c36a27a766efe9ee24fcab7130e796d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Fija=C5=82kiewicz?= Date: Thu, 21 Sep 2023 10:37:22 +0200 Subject: [PATCH 01/98] remove unnecessary search actions --- src/pages/SearchPage.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index dd42ed80c3d4..39f75d414610 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -1,5 +1,5 @@ import _ from 'underscore'; -import React, {useCallback, useEffect, useState, useMemo} from 'react'; +import React, {useCallback, useEffect, useState, useMemo, useRef} from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; @@ -56,6 +56,7 @@ function SearchPage({betas, personalDetails, reports}) { }); const {translate} = useLocalize(); + const isMounted = useRef(false); const updateOptions = useCallback(() => { const { @@ -79,8 +80,13 @@ function SearchPage({betas, personalDetails, reports}) { }, []); useEffect(() => { + if (!isMounted.current) { + isMounted.current = true; + return; + } + debouncedUpdateOptions(); - }, [searchValue, debouncedUpdateOptions]); + }, [searchValue]); /** * Returns the sections needed for the OptionsSelector From afac89ec309687a8c562317de1327256aa0b710c Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Sat, 7 Oct 2023 01:04:23 +0700 Subject: [PATCH 02/98] fix: 28969 --- src/pages/home/ReportScreen.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index 78f6edcf7dd3..faed80059507 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -157,6 +157,8 @@ function ReportScreen({ const prevUserLeavingStatus = usePrevious(userLeavingStatus); const [isBannerVisible, setIsBannerVisible] = useState(true); + const wasReportAccessibleRef = useRef(false); + const reportID = getReportID(route); const {addWorkspaceRoomOrChatPendingAction, addWorkspaceRoomOrChatErrors} = ReportUtils.getReportOfflinePendingActionAndErrors(report); const screenWrapperStyle = [styles.appContent, styles.flex1, {marginTop: viewportOffsetTop}]; @@ -179,6 +181,12 @@ function ReportScreen({ const isTopMostReportId = currentReportID === getReportID(route); const didSubscribeToReportLeavingEvents = useRef(false); + useEffect(() => { + if (report && report.reportID && !shouldHideReport) { + wasReportAccessibleRef.current = true; + } + }, [shouldHideReport, report]); + let headerView = ( (!firstRenderRef.current && !report.reportID && !isOptimisticDelete && !reportMetadata.isLoadingReportActions && !isLoading && !userLeavingStatus) || shouldHideReport, + () => (!wasReportAccessibleRef.current && !firstRenderRef.current && !report.reportID && !isOptimisticDelete && !reportMetadata.isLoadingReportActions && !isLoading && !userLeavingStatus) || shouldHideReport, [report, reportMetadata, isLoading, shouldHideReport, isOptimisticDelete, userLeavingStatus], ); From 3a20b60e93576a3b7b245a91e1ebfa997237e96d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Fija=C5=82kiewicz?= Date: Thu, 26 Oct 2023 15:32:22 +0200 Subject: [PATCH 03/98] fixes after conflicts --- src/pages/SearchPage.js | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index 3b572bcf0ebb..f9f3cc42f64a 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -18,8 +18,7 @@ import personalDetailsPropType from './personalDetailsPropType'; import reportPropTypes from './reportPropTypes'; import Performance from '../libs/Performance'; import useLocalize from '../hooks/useLocalize'; -import networkPropTypes from '../components/networkPropTypes'; -import {withNetwork} from '../components/OnyxProvider'; +import useNetwork from "../hooks/useNetwork"; const propTypes = { /* Onyx Props */ @@ -33,9 +32,6 @@ const propTypes = { /** All reports shared with the user */ reports: PropTypes.objectOf(reportPropTypes), - /** Network info */ - network: networkPropTypes, - /** Whether we are searching for reports in the server */ isSearchingForReports: PropTypes.bool, }; @@ -44,11 +40,10 @@ const defaultProps = { betas: [], personalDetails: {}, reports: {}, - network: {}, isSearchingForReports: false, }; -function SearchPage({betas, personalDetails, reports}) { +function SearchPage({betas, personalDetails, reports, isSearchingForReports}) { // Data for initialization (runs only on the first render) const { recentReports: initialRecentReports, @@ -65,6 +60,7 @@ function SearchPage({betas, personalDetails, reports}) { userToInvite: initialUserToInvite, }); + const {isOffline} = useNetwork(); const {translate} = useLocalize(); const isMounted = useRef(false); @@ -185,12 +181,12 @@ function SearchPage({betas, personalDetails, reports}) { shouldShowOptions={didScreenTransitionEnd && isOptionsDataReady} textInputLabel={translate('optionsSelector.nameEmailOrPhoneNumber')} textInputAlert={ - props.network.isOffline ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : '' + isOffline ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : '' } onLayout={searchRendered} safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle} autoFocus - isLoadingNewOptions={props.isSearchingForReports} + isLoadingNewOptions={isSearchingForReports} /> @@ -202,9 +198,7 @@ function SearchPage({betas, personalDetails, reports}) { SearchPage.propTypes = propTypes; SearchPage.defaultProps = defaultProps; SearchPage.displayName = 'SearchPage'; -export default compose( - withNetwork(), - withOnyx({ +export default withOnyx({ reports: { key: ONYXKEYS.COLLECTION.REPORT, }, @@ -218,5 +212,4 @@ export default compose( key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS, initWithStoredValues: false, }, -}), -)(SearchPage); +})(SearchPage); From e212f254a80603a169a9d37910cb66b0ebe29d9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Fija=C5=82kiewicz?= Date: Thu, 26 Oct 2023 15:42:10 +0200 Subject: [PATCH 04/98] fix lint error --- src/pages/SearchPage.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index f9f3cc42f64a..d0da509197e1 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -92,6 +92,8 @@ function SearchPage({betas, personalDetails, reports, isSearchingForReports}) { } debouncedUpdateOptions(); + // Ignoring the rule intentionally, we want to run the code only when search Value changes to prevent additional runs. + // eslint-disable-next-line react-hooks/exhaustive-deps }, [searchValue]); /** From 0cd0edc8abf862c10c1f0f38d61a17552b47041a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Fija=C5=82kiewicz?= Date: Mon, 30 Oct 2023 18:04:34 +0100 Subject: [PATCH 05/98] fix lint error --- src/pages/SearchPage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index fb325a5bc200..27148d97e9bb 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -17,8 +17,8 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import personalDetailsPropType from './personalDetailsPropType'; import reportPropTypes from './reportPropTypes'; -import useLocalize from '../hooks/useLocalize'; -import useNetwork from "../hooks/useNetwork"; +import useLocalize from '@hooks/useLocalize'; +import useNetwork from "@hooks/useNetwork"; const propTypes = { /* Onyx Props */ From f0b5986126073f4d8dab221dede715071774061a Mon Sep 17 00:00:00 2001 From: Luthfi Date: Wed, 1 Nov 2023 20:20:15 +0700 Subject: [PATCH 06/98] add form input styles checklist --- contributingGuides/REVIEWER_CHECKLIST.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contributingGuides/REVIEWER_CHECKLIST.md b/contributingGuides/REVIEWER_CHECKLIST.md index 16c8f88927b1..51908d696c1d 100644 --- a/contributingGuides/REVIEWER_CHECKLIST.md +++ b/contributingGuides/REVIEWER_CHECKLIST.md @@ -51,6 +51,9 @@ - [ ] If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like `Avatar` is modified, I verified that `Avatar` is working as expected in all cases) - [ ] If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected. - [ ] If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account. +- [ ] If the PR modifies the form input styles: + - [ ] All the inputs inside a form should be aligned with each other + - [ ] Add the Design label so the Design team can review your changes - [ ] If a new page is added, I verified it's using the `ScrollView` component to make it scrollable when more elements are added to the page. - [ ] If the `main` branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the `Test` steps. - [ ] I have checked off every checkbox in the PR reviewer checklist, including those that don't apply to this PR. From 61da6b547ba5abe206d813b7631d593e397a4de8 Mon Sep 17 00:00:00 2001 From: Luthfi Date: Wed, 1 Nov 2023 20:27:59 +0700 Subject: [PATCH 07/98] Add the checklist to the PR template --- .github/PULL_REQUEST_TEMPLATE.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 0396a7543b50..e516b8b3e0e1 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -114,6 +114,9 @@ This is a checklist for PR authors. Please make sure to complete all tasks and c - [ ] If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like `Avatar` is modified, I verified that `Avatar` is working as expected in all cases) - [ ] If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected. - [ ] If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account. +- [ ] If the PR modifies the form input styles: + - [ ] All the inputs inside a form should be aligned with each other + - [ ] Add the Design label so the Design team can review your changes - [ ] If a new page is added, I verified it's using the `ScrollView` component to make it scrollable when more elements are added to the page. - [ ] If the `main` branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the `Test` steps. - [ ] I have checked off every checkbox in the PR author checklist, including those that don't apply to this PR. From 0b0359eb5982a94b53edfedcfd6c4273d94b4416 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Fija=C5=82kiewicz?= Date: Thu, 2 Nov 2023 16:58:15 +0100 Subject: [PATCH 08/98] fix lint error --- src/pages/SearchPage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index 27148d97e9bb..70eaa0383a23 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -15,10 +15,10 @@ import * as Report from '@userActions/Report'; import Timing from '@userActions/Timing'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import personalDetailsPropType from './personalDetailsPropType'; -import reportPropTypes from './reportPropTypes'; import useLocalize from '@hooks/useLocalize'; import useNetwork from "@hooks/useNetwork"; +import personalDetailsPropType from './personalDetailsPropType'; +import reportPropTypes from './reportPropTypes'; const propTypes = { /* Onyx Props */ From 1df2c02b2f5495772d56f08def35b9ed998ab58a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Fija=C5=82kiewicz?= Date: Tue, 7 Nov 2023 08:32:22 +0100 Subject: [PATCH 09/98] prettier --- src/pages/SearchPage.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index 32d10ba539c8..3e6cb0333e3f 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -1,11 +1,13 @@ import PropTypes from 'prop-types'; -import React, {useCallback, useEffect, useState, useMemo, useRef} from 'react'; +import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import OptionsSelector from '@components/OptionsSelector'; import ScreenWrapper from '@components/ScreenWrapper'; +import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import Performance from '@libs/Performance'; @@ -15,8 +17,6 @@ import * as Report from '@userActions/Report'; import Timing from '@userActions/Timing'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import useLocalize from '@hooks/useLocalize'; -import useNetwork from "@hooks/useNetwork"; import personalDetailsPropType from './personalDetailsPropType'; import reportPropTypes from './reportPropTypes'; @@ -187,9 +187,7 @@ function SearchPage({betas, personalDetails, reports, isSearchingForReports}) { showTitleTooltip shouldShowOptions={didScreenTransitionEnd && isOptionsDataReady} textInputLabel={translate('optionsSelector.nameEmailOrPhoneNumber')} - textInputAlert={ - isOffline ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : '' - } + textInputAlert={isOffline ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''} onLayout={searchRendered} safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle} autoFocus From 8d14ca8003d9bf58639f57e7071fc0a31bacba39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Fija=C5=82kiewicz?= Date: Mon, 27 Nov 2023 23:02:00 +0100 Subject: [PATCH 10/98] refactor theme to functional way --- src/pages/SearchPage.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index 0fdd54da74f9..05405fce3cec 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -8,7 +8,7 @@ import OptionsSelector from '@components/OptionsSelector'; import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; -import withThemeStyles, {withThemeStylesPropTypes} from '@components/withThemeStyles'; +import useThemeStyles from "@styles/useThemeStyles"; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import Performance from '@libs/Performance'; @@ -34,7 +34,6 @@ const propTypes = { /** Whether we are searching for reports in the server */ isSearchingForReports: PropTypes.bool, - ...withThemeStylesPropTypes, }; const defaultProps = { @@ -63,6 +62,7 @@ function SearchPage({betas, personalDetails, reports, isSearchingForReports}) { const {isOffline} = useNetwork(); const {translate} = useLocalize(); + const themeStyles = useThemeStyles(); const isMounted = useRef(false); const updateOptions = useCallback(() => { @@ -177,7 +177,7 @@ function SearchPage({betas, personalDetails, reports, isSearchingForReports}) { {({didScreenTransitionEnd, safeAreaPaddingBottomStyle}) => ( <> - + Date: Thu, 19 Oct 2023 18:17:34 +0200 Subject: [PATCH 11/98] Migrate 'withReportAndReportActionOrNotFound.js' HOC to TypeScript --- .../withReportAndReportActionOrNotFound.js | 167 ------------------ .../withReportAndReportActionOrNotFound.tsx | 85 +++++++++ 2 files changed, 85 insertions(+), 167 deletions(-) delete mode 100644 src/pages/home/report/withReportAndReportActionOrNotFound.js create mode 100644 src/pages/home/report/withReportAndReportActionOrNotFound.tsx diff --git a/src/pages/home/report/withReportAndReportActionOrNotFound.js b/src/pages/home/report/withReportAndReportActionOrNotFound.js deleted file mode 100644 index 1450ab3fd10a..000000000000 --- a/src/pages/home/report/withReportAndReportActionOrNotFound.js +++ /dev/null @@ -1,167 +0,0 @@ -import PropTypes from 'prop-types'; -import React, {useCallback, useEffect} from 'react'; -import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; -import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; -import withWindowDimensions from '@components/withWindowDimensions'; -import compose from '@libs/compose'; -import getComponentDisplayName from '@libs/getComponentDisplayName'; -import * as ReportActionsUtils from '@libs/ReportActionsUtils'; -import * as ReportUtils from '@libs/ReportUtils'; -import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; -import reportMetadataPropTypes from '@pages/reportMetadataPropTypes'; -import reportPropTypes from '@pages/reportPropTypes'; -import * as Report from '@userActions/Report'; -import ONYXKEYS from '@src/ONYXKEYS'; -import reportActionPropTypes from './reportActionPropTypes'; - -export default function (WrappedComponent) { - const propTypes = { - /** The HOC takes an optional ref as a prop and passes it as a ref to the wrapped component. - * That way, if a ref is passed to a component wrapped in the HOC, the ref is a reference to the wrapped component, not the HOC. */ - forwardedRef: PropTypes.func, - - /** The report currently being looked at */ - report: reportPropTypes, - - /** The report metadata */ - reportMetadata: reportMetadataPropTypes, - - /** Array of report actions for this report */ - reportActions: PropTypes.shape(reportActionPropTypes), - - /** The policies which the user has access to */ - policies: PropTypes.objectOf( - PropTypes.shape({ - /** The policy name */ - name: PropTypes.string, - - /** The type of the policy */ - type: PropTypes.string, - }), - ), - - /** Route params */ - route: PropTypes.shape({ - params: PropTypes.shape({ - /** Report ID passed via route */ - reportID: PropTypes.string, - - /** ReportActionID passed via route */ - reportActionID: PropTypes.string, - }), - }).isRequired, - - /** Beta features list */ - betas: PropTypes.arrayOf(PropTypes.string), - - /** Indicated whether the report data is loading */ - isLoadingReportData: PropTypes.bool, - - /** Is the window width narrow, like on a mobile device? */ - isSmallScreenWidth: PropTypes.bool.isRequired, - }; - - const defaultProps = { - forwardedRef: () => {}, - reportActions: {}, - report: {}, - reportMetadata: { - isLoadingInitialReportActions: false, - isLoadingOlderReportActions: false, - isLoadingNewerReportActions: false, - }, - policies: {}, - betas: [], - isLoadingReportData: true, - }; - - // eslint-disable-next-line rulesdir/no-negated-variables - function WithReportAndReportActionOrNotFound(props) { - const getReportAction = useCallback(() => { - let reportAction = props.reportActions[`${props.route.params.reportActionID}`]; - - // Handle threads if needed - if (reportAction === undefined || reportAction.reportActionID === undefined) { - reportAction = ReportActionsUtils.getParentReportAction(props.report); - } - - return reportAction; - }, [props.report, props.reportActions, props.route.params.reportActionID]); - - const reportAction = getReportAction(); - - // For small screen, we don't call openReport API when we go to a sub report page by deeplink - // So we need to call openReport here for small screen - useEffect(() => { - if (!props.isSmallScreenWidth || (!_.isEmpty(props.report) && !_.isEmpty(reportAction))) { - return; - } - Report.openReport(props.route.params.reportID); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [props.isSmallScreenWidth, props.route.params.reportID]); - - // Perform all the loading checks - const isLoadingReport = props.isLoadingReportData && (_.isEmpty(props.report) || !props.report.reportID); - const isLoadingReportAction = _.isEmpty(props.reportActions) || (props.reportMetadata.isLoadingInitialReportActions && _.isEmpty(getReportAction())); - const shouldHideReport = !isLoadingReport && (_.isEmpty(props.report) || !props.report.reportID || !ReportUtils.canAccessReport(props.report, props.policies, props.betas)); - - if ((isLoadingReport || isLoadingReportAction) && !shouldHideReport) { - return ; - } - - // Perform the access/not found checks - if (shouldHideReport || _.isEmpty(reportAction)) { - return ; - } - - const rest = _.omit(props, ['forwardedRef']); - return ( - - ); - } - - WithReportAndReportActionOrNotFound.propTypes = propTypes; - WithReportAndReportActionOrNotFound.defaultProps = defaultProps; - WithReportAndReportActionOrNotFound.displayName = `withReportAndReportActionOrNotFound(${getComponentDisplayName(WrappedComponent)})`; - - // eslint-disable-next-line rulesdir/no-negated-variables - const WithReportAndReportActionOrNotFoundWithRef = React.forwardRef((props, ref) => ( - - )); - - WithReportAndReportActionOrNotFoundWithRef.displayName = 'WithReportAndReportActionOrNotFoundWithRef'; - - return compose( - withWindowDimensions, - withOnyx({ - report: { - key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`, - }, - reportMetadata: { - key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_METADATA}${route.params.reportID}`, - }, - isLoadingReportData: { - key: ONYXKEYS.IS_LOADING_REPORT_DATA, - }, - betas: { - key: ONYXKEYS.BETAS, - }, - policies: { - key: ONYXKEYS.COLLECTION.POLICY, - }, - reportActions: { - key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${route.params.reportID}`, - canEvict: false, - }, - }), - )(WithReportAndReportActionOrNotFoundWithRef); -} diff --git a/src/pages/home/report/withReportAndReportActionOrNotFound.tsx b/src/pages/home/report/withReportAndReportActionOrNotFound.tsx new file mode 100644 index 000000000000..4a73f853890c --- /dev/null +++ b/src/pages/home/report/withReportAndReportActionOrNotFound.tsx @@ -0,0 +1,85 @@ +import React, {ComponentType, ForwardedRef, RefAttributes} from 'react'; +import {withOnyx, OnyxEntry} from 'react-native-onyx'; +import {RouteProp} from '@react-navigation/native'; +import isEmpty from 'lodash/isEmpty'; +import getComponentDisplayName from '../../../libs/getComponentDisplayName'; +import NotFoundPage from '../../ErrorPage/NotFoundPage'; +import ONYXKEYS from '../../../ONYXKEYS'; +import type {Report, Policy, Beta} from '../../../types/onyx'; +import FullscreenLoadingIndicator from '../../../components/FullscreenLoadingIndicator'; +import * as ReportUtils from '../../../libs/ReportUtils'; + +type OnyxProps = { + /** The report currently being looked at */ + report: OnyxEntry; + + /** The policies which the user has access to */ + policies: OnyxEntry; + + /** Beta features list */ + betas: OnyxEntry; + + /** Indicated whether the report data is loading */ + isLoadingReportData: OnyxEntry; +}; + +type ComponentProps = OnyxProps & { + route: RouteProp<{params: {reportID: string}}>; +}; + +export default function (WrappedComponent: ComponentType>) { + // eslint-disable-next-line rulesdir/no-negated-variables + function WithReportOrNotFound(props: OnyxProps, ref: ForwardedRef) { + const contentShown = React.useRef(false); + + const shouldShowFullScreenLoadingIndicator = props.isLoadingReportData && (isEmpty(props.report) || !props.report.reportID); + // eslint-disable-next-line rulesdir/no-negated-variables + const shouldShowNotFoundPage = isEmpty(props.report) || !props.report.reportID || !ReportUtils.canAccessReport(props.report, props.policies, props.betas, {}); + + // If the content was shown but it's not anymore that means the report was deleted and we are probably navigating out of this screen. + // Return null for this case to avoid rendering FullScreenLoadingIndicator or NotFoundPage when animating transition. + if (shouldShowNotFoundPage && contentShown.current) { + return null; + } + + if (shouldShowFullScreenLoadingIndicator) { + return ; + } + + if (shouldShowNotFoundPage) { + return ; + } + + if (!contentShown.current) { + contentShown.current = true; + } + + return ( + + ); + } + + WithReportOrNotFound.displayName = `withReportOrNotFound(${getComponentDisplayName(WrappedComponent)})`; + + // eslint-disable-next-line rulesdir/no-negated-variables + const withReportOrNotFound = React.forwardRef(WithReportOrNotFound); + + return withOnyx, OnyxProps>({ + report: { + key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`, + }, + isLoadingReportData: { + key: ONYXKEYS.IS_LOADING_REPORT_DATA, + }, + betas: { + key: ONYXKEYS.BETAS, + }, + policies: { + key: ONYXKEYS.COLLECTION.POLICY, + }, + })(withReportOrNotFound); +} From 35ba4f94dfd6fa7d77fe4f5070b1311751680d71 Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Fri, 20 Oct 2023 14:21:28 +0200 Subject: [PATCH 12/98] remove isEmpty --- .../home/report/withReportAndReportActionOrNotFound.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/pages/home/report/withReportAndReportActionOrNotFound.tsx b/src/pages/home/report/withReportAndReportActionOrNotFound.tsx index 4a73f853890c..4f81ec051dc3 100644 --- a/src/pages/home/report/withReportAndReportActionOrNotFound.tsx +++ b/src/pages/home/report/withReportAndReportActionOrNotFound.tsx @@ -1,7 +1,6 @@ import React, {ComponentType, ForwardedRef, RefAttributes} from 'react'; import {withOnyx, OnyxEntry} from 'react-native-onyx'; import {RouteProp} from '@react-navigation/native'; -import isEmpty from 'lodash/isEmpty'; import getComponentDisplayName from '../../../libs/getComponentDisplayName'; import NotFoundPage from '../../ErrorPage/NotFoundPage'; import ONYXKEYS from '../../../ONYXKEYS'; @@ -29,12 +28,12 @@ type ComponentProps = OnyxProps & { export default function (WrappedComponent: ComponentType>) { // eslint-disable-next-line rulesdir/no-negated-variables - function WithReportOrNotFound(props: OnyxProps, ref: ForwardedRef) { + function WithReportOrNotFound(props: TProps, ref: ForwardedRef) { const contentShown = React.useRef(false); - const shouldShowFullScreenLoadingIndicator = props.isLoadingReportData && (isEmpty(props.report) || !props.report.reportID); + const shouldShowFullScreenLoadingIndicator = props.isLoadingReportData && (!Object.entries(props.report ?? {}).length || !props.report?.reportID); // eslint-disable-next-line rulesdir/no-negated-variables - const shouldShowNotFoundPage = isEmpty(props.report) || !props.report.reportID || !ReportUtils.canAccessReport(props.report, props.policies, props.betas, {}); + const shouldShowNotFoundPage = !Object.entries(props.report ?? {}).length || !props.report?.reportID || !ReportUtils.canAccessReport(props.report, props.policies, props.betas, {}); // If the content was shown but it's not anymore that means the report was deleted and we are probably navigating out of this screen. // Return null for this case to avoid rendering FullScreenLoadingIndicator or NotFoundPage when animating transition. @@ -57,7 +56,7 @@ export default function (WrappedComponent: return ( ); From 21543061c429a5c7708c9b207ab09d580bf85611 Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Thu, 16 Nov 2023 18:55:02 +0100 Subject: [PATCH 13/98] update migration to latest version of file --- .../withReportAndReportActionOrNotFound.tsx | 127 +++++++++++------- 1 file changed, 80 insertions(+), 47 deletions(-) diff --git a/src/pages/home/report/withReportAndReportActionOrNotFound.tsx b/src/pages/home/report/withReportAndReportActionOrNotFound.tsx index 4f81ec051dc3..44b4985d0850 100644 --- a/src/pages/home/report/withReportAndReportActionOrNotFound.tsx +++ b/src/pages/home/report/withReportAndReportActionOrNotFound.tsx @@ -1,58 +1,84 @@ -import React, {ComponentType, ForwardedRef, RefAttributes} from 'react'; -import {withOnyx, OnyxEntry} from 'react-native-onyx'; +/* eslint-disable rulesdir/no-negated-variables */ import {RouteProp} from '@react-navigation/native'; -import getComponentDisplayName from '../../../libs/getComponentDisplayName'; -import NotFoundPage from '../../ErrorPage/NotFoundPage'; -import ONYXKEYS from '../../../ONYXKEYS'; -import type {Report, Policy, Beta} from '../../../types/onyx'; -import FullscreenLoadingIndicator from '../../../components/FullscreenLoadingIndicator'; -import * as ReportUtils from '../../../libs/ReportUtils'; +import React, {ComponentType, ForwardedRef, RefAttributes, useCallback, useEffect} from 'react'; +import {OnyxCollection, OnyxEntry, withOnyx} from 'react-native-onyx'; +import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; +import withWindowDimensions from '@components/withWindowDimensions'; +import {WindowDimensionsProps} from '@components/withWindowDimensions/types'; +import compose from '@libs/compose'; +import getComponentDisplayName from '@libs/getComponentDisplayName'; +import * as ReportActionsUtils from '@libs/ReportActionsUtils'; +import * as ReportUtils from '@libs/ReportUtils'; +import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; +import * as Report from '@userActions/Report'; +import ONYXKEYS from '@src/ONYXKEYS'; +import * as OnyxTypes from '@src/types/onyx'; +import {isEmptyObjectOrString} from '@src/types/utils/EmptyObject'; type OnyxProps = { /** The report currently being looked at */ - report: OnyxEntry; + report: OnyxEntry; + + /** The report metadata */ + reportMetadata: OnyxEntry; + + /** Array of report actions for this report */ + reportActions: OnyxEntry; /** The policies which the user has access to */ - policies: OnyxEntry; + policies: OnyxCollection; /** Beta features list */ - betas: OnyxEntry; + betas: OnyxEntry; /** Indicated whether the report data is loading */ isLoadingReportData: OnyxEntry; }; -type ComponentProps = OnyxProps & { - route: RouteProp<{params: {reportID: string}}>; -}; +type ComponentProps = OnyxProps & + WindowDimensionsProps & { + route: RouteProp<{params: {reportID: string; reportActionID: string}}>; + }; export default function (WrappedComponent: ComponentType>) { - // eslint-disable-next-line rulesdir/no-negated-variables function WithReportOrNotFound(props: TProps, ref: ForwardedRef) { - const contentShown = React.useRef(false); + const getReportAction = useCallback(() => { + let reportAction: OnyxTypes.ReportAction | Record | undefined = props.reportActions?.[`${props.route.params.reportActionID}`]; - const shouldShowFullScreenLoadingIndicator = props.isLoadingReportData && (!Object.entries(props.report ?? {}).length || !props.report?.reportID); - // eslint-disable-next-line rulesdir/no-negated-variables - const shouldShowNotFoundPage = !Object.entries(props.report ?? {}).length || !props.report?.reportID || !ReportUtils.canAccessReport(props.report, props.policies, props.betas, {}); + // Handle threads if needed + if (!reportAction || !reportAction.reportActionID) { + reportAction = ReportActionsUtils.getParentReportAction(props.report); + } - // If the content was shown but it's not anymore that means the report was deleted and we are probably navigating out of this screen. - // Return null for this case to avoid rendering FullScreenLoadingIndicator or NotFoundPage when animating transition. - if (shouldShowNotFoundPage && contentShown.current) { - return null; - } + return reportAction; + }, [props.report, props.reportActions, props.route.params.reportActionID]); + + const reportAction = getReportAction(); - if (shouldShowFullScreenLoadingIndicator) { + // For small screen, we don't call openReport API when we go to a sub report page by deeplink + // So we need to call openReport here for small screen + useEffect(() => { + if (!props.isSmallScreenWidth || (isEmptyObjectOrString(props.report) && isEmptyObjectOrString(reportAction))) { + return; + } + Report.openReport(props.route.params.reportID); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [props.isSmallScreenWidth, props.route.params.reportID]); + + // Perform all the loading checks + const isLoadingReport = props.isLoadingReportData && isEmptyObjectOrString(props.report?.reportID); + const isLoadingReportAction = isEmptyObjectOrString(props.reportActions) || (props.reportMetadata?.isLoadingInitialReportActions && isEmptyObjectOrString(getReportAction())); + const shouldHideReport = !isLoadingReport && (isEmptyObjectOrString(props.report?.reportID) || !ReportUtils.canAccessReport(props.report, props.policies, props.betas, {})); + + if ((isLoadingReport ?? isLoadingReportAction) && !shouldHideReport) { return ; } - if (shouldShowNotFoundPage) { + // Perform the access/not found checks + if (shouldHideReport || isEmptyObjectOrString(reportAction)) { return ; } - if (!contentShown.current) { - contentShown.current = true; - } - return ( (WrappedComponent: WithReportOrNotFound.displayName = `withReportOrNotFound(${getComponentDisplayName(WrappedComponent)})`; - // eslint-disable-next-line rulesdir/no-negated-variables - const withReportOrNotFound = React.forwardRef(WithReportOrNotFound); - - return withOnyx, OnyxProps>({ - report: { - key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`, - }, - isLoadingReportData: { - key: ONYXKEYS.IS_LOADING_REPORT_DATA, - }, - betas: { - key: ONYXKEYS.BETAS, - }, - policies: { - key: ONYXKEYS.COLLECTION.POLICY, - }, - })(withReportOrNotFound); + return compose( + withOnyx, OnyxProps>({ + report: { + key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`, + }, + reportMetadata: { + key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_METADATA}${route.params.reportID}`, + }, + isLoadingReportData: { + key: ONYXKEYS.IS_LOADING_REPORT_DATA, + }, + betas: { + key: ONYXKEYS.BETAS, + }, + policies: { + key: ONYXKEYS.COLLECTION.POLICY, + }, + reportActions: { + key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${route.params.reportID}`, + canEvict: false, + }, + }), + withWindowDimensions, + )(React.forwardRef(WithReportOrNotFound)); } From 473416f9812c7eeeda9345a7812dc6ec1c2b26ca Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Fri, 17 Nov 2023 18:01:35 +0100 Subject: [PATCH 14/98] update type signature --- src/types/utils/EmptyObject.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/types/utils/EmptyObject.ts b/src/types/utils/EmptyObject.ts index 9b9f3244a5f8..3ddb6c8880e1 100644 --- a/src/types/utils/EmptyObject.ts +++ b/src/types/utils/EmptyObject.ts @@ -1,9 +1,7 @@ import Falsy from './Falsy'; -type EmptyObject = Record; - // eslint-disable-next-line rulesdir/no-negated-variables -function isNotEmptyObject | Falsy>(arg: T | EmptyObject): arg is NonNullable { +function isNotEmptyObject | Falsy>(arg: T): arg is NonNullable { return Object.keys(arg ?? {}).length > 0; } @@ -12,4 +10,3 @@ function isEmptyObject(obj: T): boolean { } export {isNotEmptyObject, isEmptyObject}; -export type {EmptyObject}; From 04f51344828271c4bb7fd76dd31bfaa5fe3c1479 Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Tue, 28 Nov 2023 16:05:32 +0100 Subject: [PATCH 15/98] fix comments --- src/pages/home/report/withReportAndReportActionOrNotFound.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/home/report/withReportAndReportActionOrNotFound.tsx b/src/pages/home/report/withReportAndReportActionOrNotFound.tsx index 44b4985d0850..c483606a8e0b 100644 --- a/src/pages/home/report/withReportAndReportActionOrNotFound.tsx +++ b/src/pages/home/report/withReportAndReportActionOrNotFound.tsx @@ -46,7 +46,7 @@ export default function (WrappedComponent: let reportAction: OnyxTypes.ReportAction | Record | undefined = props.reportActions?.[`${props.route.params.reportActionID}`]; // Handle threads if needed - if (!reportAction || !reportAction.reportActionID) { + if (!reportAction?.reportActionID) { reportAction = ReportActionsUtils.getParentReportAction(props.report); } @@ -68,7 +68,7 @@ export default function (WrappedComponent: // Perform all the loading checks const isLoadingReport = props.isLoadingReportData && isEmptyObjectOrString(props.report?.reportID); const isLoadingReportAction = isEmptyObjectOrString(props.reportActions) || (props.reportMetadata?.isLoadingInitialReportActions && isEmptyObjectOrString(getReportAction())); - const shouldHideReport = !isLoadingReport && (isEmptyObjectOrString(props.report?.reportID) || !ReportUtils.canAccessReport(props.report, props.policies, props.betas, {})); + const shouldHideReport = !isLoadingReport && (isEmptyObjectOrString(props.report?.reportID) || !ReportUtils.canAccessReport(props.report, props.policies, props.betas, null)); if ((isLoadingReport ?? isLoadingReportAction) && !shouldHideReport) { return ; From e8b91bdd1b5211328185237f288a9914bcb9f930 Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Tue, 28 Nov 2023 16:51:59 +0100 Subject: [PATCH 16/98] revert EmptyObject --- src/types/utils/EmptyObject.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/types/utils/EmptyObject.ts b/src/types/utils/EmptyObject.ts index 3ddb6c8880e1..9b9f3244a5f8 100644 --- a/src/types/utils/EmptyObject.ts +++ b/src/types/utils/EmptyObject.ts @@ -1,7 +1,9 @@ import Falsy from './Falsy'; +type EmptyObject = Record; + // eslint-disable-next-line rulesdir/no-negated-variables -function isNotEmptyObject | Falsy>(arg: T): arg is NonNullable { +function isNotEmptyObject | Falsy>(arg: T | EmptyObject): arg is NonNullable { return Object.keys(arg ?? {}).length > 0; } @@ -10,3 +12,4 @@ function isEmptyObject(obj: T): boolean { } export {isNotEmptyObject, isEmptyObject}; +export type {EmptyObject}; From ee5369e3e423eb813835ad84c001cb18ebec6bbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Fija=C5=82kiewicz?= Date: Tue, 28 Nov 2023 21:30:59 +0100 Subject: [PATCH 17/98] prettier --- src/pages/SearchPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index 05405fce3cec..b0eb45876520 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -8,11 +8,11 @@ import OptionsSelector from '@components/OptionsSelector'; import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; -import useThemeStyles from "@styles/useThemeStyles"; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import Performance from '@libs/Performance'; import * as ReportUtils from '@libs/ReportUtils'; +import useThemeStyles from '@styles/useThemeStyles'; import * as Report from '@userActions/Report'; import Timing from '@userActions/Timing'; import CONST from '@src/CONST'; From 870561de7e3ff7f1392c1df87ff132bfb9fea9c8 Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Wed, 29 Nov 2023 13:04:51 +0100 Subject: [PATCH 18/98] update is empty method --- .../report/withReportAndReportActionOrNotFound.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pages/home/report/withReportAndReportActionOrNotFound.tsx b/src/pages/home/report/withReportAndReportActionOrNotFound.tsx index c483606a8e0b..3f3547b5207a 100644 --- a/src/pages/home/report/withReportAndReportActionOrNotFound.tsx +++ b/src/pages/home/report/withReportAndReportActionOrNotFound.tsx @@ -13,7 +13,7 @@ import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; import * as Report from '@userActions/Report'; import ONYXKEYS from '@src/ONYXKEYS'; import * as OnyxTypes from '@src/types/onyx'; -import {isEmptyObjectOrString} from '@src/types/utils/EmptyObject'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; type OnyxProps = { /** The report currently being looked at */ @@ -58,7 +58,7 @@ export default function (WrappedComponent: // For small screen, we don't call openReport API when we go to a sub report page by deeplink // So we need to call openReport here for small screen useEffect(() => { - if (!props.isSmallScreenWidth || (isEmptyObjectOrString(props.report) && isEmptyObjectOrString(reportAction))) { + if (!props.isSmallScreenWidth || (isEmptyObject(props.report) && isEmptyObject(reportAction))) { return; } Report.openReport(props.route.params.reportID); @@ -66,16 +66,16 @@ export default function (WrappedComponent: }, [props.isSmallScreenWidth, props.route.params.reportID]); // Perform all the loading checks - const isLoadingReport = props.isLoadingReportData && isEmptyObjectOrString(props.report?.reportID); - const isLoadingReportAction = isEmptyObjectOrString(props.reportActions) || (props.reportMetadata?.isLoadingInitialReportActions && isEmptyObjectOrString(getReportAction())); - const shouldHideReport = !isLoadingReport && (isEmptyObjectOrString(props.report?.reportID) || !ReportUtils.canAccessReport(props.report, props.policies, props.betas, null)); + const isLoadingReport = props.isLoadingReportData && props.report?.reportID; + const isLoadingReportAction = isEmptyObject(props.reportActions) || (props.reportMetadata?.isLoadingInitialReportActions && isEmptyObject(getReportAction())); + const shouldHideReport = !isLoadingReport && (isEmptyObject(props.report?.reportID) || !ReportUtils.canAccessReport(props.report, props.policies, props.betas, null)); if ((isLoadingReport ?? isLoadingReportAction) && !shouldHideReport) { return ; } // Perform the access/not found checks - if (shouldHideReport || isEmptyObjectOrString(reportAction)) { + if (shouldHideReport || isEmptyObject(reportAction)) { return ; } From 12c20698746ad3c96784c47fbbea6831fbba702a Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Wed, 29 Nov 2023 13:34:56 +0100 Subject: [PATCH 19/98] add return type --- src/pages/home/report/withReportAndReportActionOrNotFound.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/withReportAndReportActionOrNotFound.tsx b/src/pages/home/report/withReportAndReportActionOrNotFound.tsx index 3f3547b5207a..63e434a9e73f 100644 --- a/src/pages/home/report/withReportAndReportActionOrNotFound.tsx +++ b/src/pages/home/report/withReportAndReportActionOrNotFound.tsx @@ -40,7 +40,7 @@ type ComponentProps = OnyxProps & route: RouteProp<{params: {reportID: string; reportActionID: string}}>; }; -export default function (WrappedComponent: ComponentType>) { +export default function (WrappedComponent: ComponentType>): ComponentType> { function WithReportOrNotFound(props: TProps, ref: ForwardedRef) { const getReportAction = useCallback(() => { let reportAction: OnyxTypes.ReportAction | Record | undefined = props.reportActions?.[`${props.route.params.reportActionID}`]; From 8231d4f6c7c9b69a46425a7cdb08506bbe024810 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 6 Dec 2023 20:50:26 +0700 Subject: [PATCH 20/98] fix infinite loading when deleting money report --- src/pages/home/ReportScreen.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index c5f8e2b883dc..d8c8734bb362 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -186,10 +186,12 @@ function ReportScreen({ const isTopMostReportId = currentReportID === getReportID(route); const didSubscribeToReportLeavingEvents = useRef(false); + useEffect(() => { - if (report && report.reportID && !shouldHideReport) { - wasReportAccessibleRef.current = true; + if (!report || !report.reportID || shouldHideReport) { + return; } + wasReportAccessibleRef.current = true; }, [shouldHideReport, report]); const goBack = useCallback(() => { @@ -311,7 +313,6 @@ function ReportScreen({ const onyxReportID = report.reportID; const prevOnyxReportID = prevReport.reportID; const routeReportID = getReportID(route); - // Navigate to the Concierge chat if the room was removed from another device (e.g. user leaving a room or removed from a room) if ( // non-optimistic case @@ -321,7 +322,8 @@ function ReportScreen({ prevOnyxReportID === routeReportID && !onyxReportID && prevReport.statusNum === CONST.REPORT.STATUS.OPEN && - (report.statusNum === CONST.REPORT.STATUS.CLOSED || (!report.statusNum && !prevReport.parentReportID && prevReport.chatType === CONST.REPORT.CHAT_TYPE.POLICY_ROOM))) + (report.statusNum === CONST.REPORT.STATUS.CLOSED || (!report.statusNum && !prevReport.parentReportID && prevReport.chatType === CONST.REPORT.CHAT_TYPE.POLICY_ROOM))) || + (ReportUtils.isMoneyRequest(prevReport) && _.isEqual(report, defaultProps.report)) ) { Navigation.dismissModal(); if (Navigation.getTopmostReportId() === prevOnyxReportID) { From 8ad959ad0a5507b4b940cef05c4588cc67fc15ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Fija=C5=82kiewicz?= Date: Fri, 8 Dec 2023 14:56:15 +0100 Subject: [PATCH 21/98] bring back accidentally deleted code --- src/pages/SearchPage.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index 817fb7a231d1..68724b51e796 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -189,6 +189,8 @@ function SearchPage({betas, personalDetails, reports, isSearchingForReports}) { showTitleTooltip shouldShowOptions={didScreenTransitionEnd && isOptionsDataReady} textInputLabel={translate('optionsSelector.nameEmailOrPhoneNumber')} + shouldShowReferralCTA + referralContentType={CONST.REFERRAL_PROGRAM.CONTENT_TYPES.REFER_FRIEND} textInputAlert={isOffline ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''} onLayout={searchRendered} safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle} From eec08575f2343d92472ab4b1a97bb3c25ec7fac9 Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Fri, 8 Dec 2023 18:07:09 +0100 Subject: [PATCH 22/98] apply comments --- src/pages/home/report/withReportAndReportActionOrNotFound.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/home/report/withReportAndReportActionOrNotFound.tsx b/src/pages/home/report/withReportAndReportActionOrNotFound.tsx index 63e434a9e73f..f4dc70af8720 100644 --- a/src/pages/home/report/withReportAndReportActionOrNotFound.tsx +++ b/src/pages/home/report/withReportAndReportActionOrNotFound.tsx @@ -66,9 +66,9 @@ export default function (WrappedComponent: }, [props.isSmallScreenWidth, props.route.params.reportID]); // Perform all the loading checks - const isLoadingReport = props.isLoadingReportData && props.report?.reportID; + const isLoadingReport = props.isLoadingReportData && !props.report?.reportID; const isLoadingReportAction = isEmptyObject(props.reportActions) || (props.reportMetadata?.isLoadingInitialReportActions && isEmptyObject(getReportAction())); - const shouldHideReport = !isLoadingReport && (isEmptyObject(props.report?.reportID) || !ReportUtils.canAccessReport(props.report, props.policies, props.betas, null)); + const shouldHideReport = !isLoadingReport && (!props.report?.reportID || !ReportUtils.canAccessReport(props.report, props.policies, props.betas, null)); if ((isLoadingReport ?? isLoadingReportAction) && !shouldHideReport) { return ; From c8788706e18ee4869be6b6161fd1fd01a884a94b Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Mon, 11 Dec 2023 14:01:36 +0100 Subject: [PATCH 23/98] fix typecheck fail --- src/pages/home/report/withReportAndReportActionOrNotFound.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/withReportAndReportActionOrNotFound.tsx b/src/pages/home/report/withReportAndReportActionOrNotFound.tsx index f4dc70af8720..7fb222334a8a 100644 --- a/src/pages/home/report/withReportAndReportActionOrNotFound.tsx +++ b/src/pages/home/report/withReportAndReportActionOrNotFound.tsx @@ -68,7 +68,7 @@ export default function (WrappedComponent: // Perform all the loading checks const isLoadingReport = props.isLoadingReportData && !props.report?.reportID; const isLoadingReportAction = isEmptyObject(props.reportActions) || (props.reportMetadata?.isLoadingInitialReportActions && isEmptyObject(getReportAction())); - const shouldHideReport = !isLoadingReport && (!props.report?.reportID || !ReportUtils.canAccessReport(props.report, props.policies, props.betas, null)); + const shouldHideReport = !isLoadingReport && (!props.report?.reportID || !ReportUtils.canAccessReport(props.report, props.policies, props.betas)); if ((isLoadingReport ?? isLoadingReportAction) && !shouldHideReport) { return ; From 103821be9d3ba154ee046efc36742ac13f15da62 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Mon, 11 Dec 2023 15:39:51 +0100 Subject: [PATCH 24/98] migrate LinkPreviewer to typescript --- src/pages/home/report/LinkPreviewer.js | 139 ------------------------ src/pages/home/report/LinkPreviewer.tsx | 120 ++++++++++++++++++++ 2 files changed, 120 insertions(+), 139 deletions(-) delete mode 100644 src/pages/home/report/LinkPreviewer.js create mode 100644 src/pages/home/report/LinkPreviewer.tsx diff --git a/src/pages/home/report/LinkPreviewer.js b/src/pages/home/report/LinkPreviewer.js deleted file mode 100644 index 5c16d5596774..000000000000 --- a/src/pages/home/report/LinkPreviewer.js +++ /dev/null @@ -1,139 +0,0 @@ -import {uniqBy} from 'lodash'; -import PropTypes from 'prop-types'; -import React from 'react'; -import {Image, View} from 'react-native'; -import _ from 'underscore'; -import Text from '@components/Text'; -import TextLink from '@components/TextLink'; -import useTheme from '@styles/themes/useTheme'; -import useStyleUtils from '@styles/useStyleUtils'; -import useThemeStyles from '@styles/useThemeStyles'; -import variables from '@styles/variables'; - -const IMAGE_TYPES = ['jpg', 'jpeg', 'png']; -const MAX_IMAGE_HEIGHT = 180; -const MAX_IMAGE_WIDTH = 340; - -const propTypes = { - /** Data about links provided in message. */ - linkMetadata: PropTypes.arrayOf( - PropTypes.shape({ - /** The URL of the link. */ - url: PropTypes.string, - - /** A description of the link. */ - description: PropTypes.string, - - /** The title of the link. */ - title: PropTypes.string, - - /** The publisher of the link. */ - publisher: PropTypes.string, - - /** The image associated with the link. */ - image: PropTypes.shape({ - /** The height of the image. */ - height: PropTypes.number, - - /** The width of the image. */ - width: PropTypes.number, - - /** The URL of the image. */ - url: PropTypes.string, - }), - - /** The provider logo associated with the link. */ - logo: PropTypes.shape({ - /** The height of the logo. */ - height: PropTypes.number, - - /** The width of the logo. */ - width: PropTypes.number, - - /** The URL of the logo. */ - url: PropTypes.string, - }), - }), - ), - - /** Maximum amount of visible link previews. -1 means unlimited amount of previews */ - maxAmountOfPreviews: PropTypes.number, -}; - -const defaultProps = { - linkMetadata: [], - maxAmountOfPreviews: -1, -}; - -function LinkPreviewer(props) { - const theme = useTheme(); - const styles = useThemeStyles(); - const StyleUtils = useStyleUtils(); - return _.map( - _.take(uniqBy(props.linkMetadata, 'url'), props.maxAmountOfPreviews >= 0 ? Math.min(props.maxAmountOfPreviews, props.linkMetadata.length) : props.linkMetadata.length), - (linkData) => { - if (_.isArray(linkData)) { - return; - } - const {description, image, title, logo, publisher, url} = linkData; - - return ( - linkData && ( - - - {!_.isEmpty(logo) && ( - - )} - {!_.isEmpty(publisher) && ( - - {publisher} - - )} - - {!_.isEmpty(title) && ( - - {title} - - )} - {!_.isEmpty(description) && {description}} - {!_.isEmpty(image) && IMAGE_TYPES.includes(image.type) && ( - - )} - - ) - ); - }, - ); -} - -LinkPreviewer.propTypes = propTypes; -LinkPreviewer.defaultProps = defaultProps; -LinkPreviewer.displayName = 'ReportLinkPreview'; - -export default LinkPreviewer; diff --git a/src/pages/home/report/LinkPreviewer.tsx b/src/pages/home/report/LinkPreviewer.tsx new file mode 100644 index 000000000000..c9f50ec91a48 --- /dev/null +++ b/src/pages/home/report/LinkPreviewer.tsx @@ -0,0 +1,120 @@ +import React from 'react'; +import {Image, View} from 'react-native'; +import Text from '@components/Text'; +import TextLink from '@components/TextLink'; +import useTheme from '@styles/themes/useTheme'; +import useStyleUtils from '@styles/useStyleUtils'; +import useThemeStyles from '@styles/useThemeStyles'; +import variables from '@styles/variables'; + +const IMAGE_TYPES = ['jpg', 'jpeg', 'png']; +const MAX_IMAGE_HEIGHT = 180; +const MAX_IMAGE_WIDTH = 340; + +type ImageData = { + /** The height of the image. */ + height: number; + + /** The width of the image. */ + width: number; + + /** The URL of the image. */ + url: string; + + /** The type of the image. */ + type?: string; +}; + +type LinkMetadata = { + /** The URL of the link. */ + url?: string; + + /** A description of the link. */ + description?: string; + + /** The title of the link. */ + title?: string; + + /** The publisher of the link. */ + publisher?: string; + + /** The image associated with the link. */ + image?: ImageData; + + /** The provider logo associated with the link. */ + logo?: ImageData; +}; + +type LinkPreviewerProps = { + /** Data about links provided in message. */ + linkMetadata?: LinkMetadata[]; + + /** Maximum amount of visible link previews. -1 means unlimited amount of previews */ + maxAmountOfPreviews?: number; +}; + +function LinkPreviewer({linkMetadata = [], maxAmountOfPreviews = -1}: LinkPreviewerProps) { + const theme = useTheme(); + const styles = useThemeStyles(); + const StyleUtils = useStyleUtils(); + const uniqueLinks = linkMetadata.filter((link, index, self) => self.findIndex((t) => t.url === link.url) === index); + const linksToShow = uniqueLinks.slice(0, maxAmountOfPreviews >= 0 ? Math.min(maxAmountOfPreviews, linkMetadata.length) : linkMetadata.length); + return linksToShow.map((linkData) => { + const {description, image, title, logo, publisher, url} = linkData; + return ( + linkData && ( + + + {logo && ( + + )} + {publisher && ( + + {publisher} + + )} + + {title && url && ( + + {title} + + )} + {description && {description}} + {image?.type && IMAGE_TYPES.includes(image.type) && ( + + )} + + ) + ); + }); +} + +LinkPreviewer.displayName = 'ReportLinkPreview'; + +export default LinkPreviewer; From d3fb9ba8aa560937ab0bd058eefdd9c9d29b352d Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Fri, 1 Dec 2023 13:45:56 +0100 Subject: [PATCH 25/98] Migrate 'SpacerView.js' component to TypeScript --- .../{SpacerView.js => SpacerView.tsx} | 23 +++++-------------- 1 file changed, 6 insertions(+), 17 deletions(-) rename src/components/{SpacerView.js => SpacerView.tsx} (76%) diff --git a/src/components/SpacerView.js b/src/components/SpacerView.tsx similarity index 76% rename from src/components/SpacerView.js rename to src/components/SpacerView.tsx index b9705a15b5f3..938036feb9ac 100644 --- a/src/components/SpacerView.js +++ b/src/components/SpacerView.tsx @@ -1,34 +1,25 @@ -import PropTypes from 'prop-types'; import React from 'react'; +import {StyleProp, ViewStyle} from 'react-native'; import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated'; import usePrevious from '@hooks/usePrevious'; -import stylePropTypes from '@styles/stylePropTypes'; -import useStyleUtils from '@styles/useStyleUtils'; import CONST from '@src/CONST'; -const propTypes = { +type SpacerViewProps = { /** * Should we show the spacer */ - shouldShow: PropTypes.bool.isRequired, + shouldShow: boolean; /** * Array of style objects - * @default [] */ - style: stylePropTypes, + style?: StyleProp; }; -const defaultProps = { - style: [], -}; - -function SpacerView({shouldShow = true, style = []}) { - const StyleUtils = useStyleUtils(); +function SpacerView({shouldShow = true, style = []}: SpacerViewProps) { const marginVertical = useSharedValue(shouldShow ? CONST.HORIZONTAL_SPACER.DEFAULT_MARGIN_VERTICAL : CONST.HORIZONTAL_SPACER.HIDDEN_MARGIN_VERTICAL); const borderBottomWidth = useSharedValue(shouldShow ? CONST.HORIZONTAL_SPACER.DEFAULT_BORDER_BOTTOM_WIDTH : CONST.HORIZONTAL_SPACER.HIDDEN_BORDER_BOTTOM_WIDTH); const prevShouldShow = usePrevious(shouldShow); - const duration = CONST.ANIMATED_TRANSITION; const animatedStyles = useAnimatedStyle(() => ({ borderBottomWidth: withTiming(borderBottomWidth.value, {duration}), @@ -50,10 +41,8 @@ function SpacerView({shouldShow = true, style = []}) { // eslint-disable-next-line react-hooks/exhaustive-deps -- we only need to trigger when shouldShow prop is changed }, [shouldShow, prevShouldShow]); - return ; + return ; } SpacerView.displayName = 'SpacerView'; -SpacerView.propTypes = propTypes; -SpacerView.defaultProps = defaultProps; export default SpacerView; From 426973c85c2c6000978c711972918038dc090b64 Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Mon, 4 Dec 2023 17:48:29 +0100 Subject: [PATCH 26/98] apply comments --- src/components/SpacerView.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/SpacerView.tsx b/src/components/SpacerView.tsx index 938036feb9ac..4c54aef61ba2 100644 --- a/src/components/SpacerView.tsx +++ b/src/components/SpacerView.tsx @@ -16,9 +16,9 @@ type SpacerViewProps = { style?: StyleProp; }; -function SpacerView({shouldShow = true, style = []}: SpacerViewProps) { - const marginVertical = useSharedValue(shouldShow ? CONST.HORIZONTAL_SPACER.DEFAULT_MARGIN_VERTICAL : CONST.HORIZONTAL_SPACER.HIDDEN_MARGIN_VERTICAL); - const borderBottomWidth = useSharedValue(shouldShow ? CONST.HORIZONTAL_SPACER.DEFAULT_BORDER_BOTTOM_WIDTH : CONST.HORIZONTAL_SPACER.HIDDEN_BORDER_BOTTOM_WIDTH); +function SpacerView({shouldShow, style}: SpacerViewProps) { + const marginVertical = useSharedValue(shouldShow ? CONST.HORIZONTAL_SPACER.DEFAULT_MARGIN_VERTICAL : CONST.HORIZONTAL_SPACER.HIDDEN_MARGIN_VERTICAL); + const borderBottomWidth = useSharedValue(shouldShow ? CONST.HORIZONTAL_SPACER.DEFAULT_BORDER_BOTTOM_WIDTH : CONST.HORIZONTAL_SPACER.HIDDEN_BORDER_BOTTOM_WIDTH); const prevShouldShow = usePrevious(shouldShow); const duration = CONST.ANIMATED_TRANSITION; const animatedStyles = useAnimatedStyle(() => ({ @@ -45,4 +45,5 @@ function SpacerView({shouldShow = true, style = []}: SpacerViewProps) { } SpacerView.displayName = 'SpacerView'; + export default SpacerView; From a19b7d4f3472d24c92217dc8002fb0b97b597c96 Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Wed, 6 Dec 2023 12:27:05 +0100 Subject: [PATCH 27/98] Migrate 'AnimatedEmptyStateBackground.js' component to TypeScript --- ...EmptyStateBackground.js => AnimatedEmptyStateBackground.tsx} | 0 src/styles/utils/index.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/pages/home/report/{AnimatedEmptyStateBackground.js => AnimatedEmptyStateBackground.tsx} (100%) diff --git a/src/pages/home/report/AnimatedEmptyStateBackground.js b/src/pages/home/report/AnimatedEmptyStateBackground.tsx similarity index 100% rename from src/pages/home/report/AnimatedEmptyStateBackground.js rename to src/pages/home/report/AnimatedEmptyStateBackground.tsx diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index 8d52c8de200a..20c5eaf19eee 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -728,7 +728,7 @@ function getHorizontalStackedOverlayAvatarStyle(oneAvatarSize: AvatarSize, oneAv /** * Gets the correct size for the empty state background image based on screen dimensions */ -function getReportWelcomeBackgroundImageStyle(isSmallScreenWidth: boolean, isMoneyReport = false): ViewStyle { +function getReportWelcomeBackgroundImageStyle(isSmallScreenWidth: boolean, isMoneyReport = false): ImageStyle { const emptyStateBackground = isMoneyReport ? CONST.EMPTY_STATE_BACKGROUND.MONEY_REPORT : CONST.EMPTY_STATE_BACKGROUND; if (isSmallScreenWidth) { From 20463d1c466e0632c0878636811f7f01470f6b62 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Wed, 13 Dec 2023 10:15:53 +0100 Subject: [PATCH 28/98] fix PR comments --- src/pages/home/report/LinkPreviewer.tsx | 41 ++++--------------------- src/types/onyx/ReportAction.ts | 36 +++++++++++++++++++++- 2 files changed, 41 insertions(+), 36 deletions(-) diff --git a/src/pages/home/report/LinkPreviewer.tsx b/src/pages/home/report/LinkPreviewer.tsx index c9f50ec91a48..cdc7bfff23be 100644 --- a/src/pages/home/report/LinkPreviewer.tsx +++ b/src/pages/home/report/LinkPreviewer.tsx @@ -6,45 +6,12 @@ import useTheme from '@styles/themes/useTheme'; import useStyleUtils from '@styles/useStyleUtils'; import useThemeStyles from '@styles/useThemeStyles'; import variables from '@styles/variables'; +import type {LinkMetadata} from '@src/types/onyx/ReportAction'; const IMAGE_TYPES = ['jpg', 'jpeg', 'png']; const MAX_IMAGE_HEIGHT = 180; const MAX_IMAGE_WIDTH = 340; -type ImageData = { - /** The height of the image. */ - height: number; - - /** The width of the image. */ - width: number; - - /** The URL of the image. */ - url: string; - - /** The type of the image. */ - type?: string; -}; - -type LinkMetadata = { - /** The URL of the link. */ - url?: string; - - /** A description of the link. */ - description?: string; - - /** The title of the link. */ - title?: string; - - /** The publisher of the link. */ - publisher?: string; - - /** The image associated with the link. */ - image?: ImageData; - - /** The provider logo associated with the link. */ - logo?: ImageData; -}; - type LinkPreviewerProps = { /** Data about links provided in message. */ linkMetadata?: LinkMetadata[]; @@ -58,8 +25,12 @@ function LinkPreviewer({linkMetadata = [], maxAmountOfPreviews = -1}: LinkPrevie const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const uniqueLinks = linkMetadata.filter((link, index, self) => self.findIndex((t) => t.url === link.url) === index); - const linksToShow = uniqueLinks.slice(0, maxAmountOfPreviews >= 0 ? Math.min(maxAmountOfPreviews, linkMetadata.length) : linkMetadata.length); + const maxAmmountOfLinks = maxAmountOfPreviews >= 0 ? Math.min(maxAmountOfPreviews, linkMetadata.length) : linkMetadata.length; + const linksToShow = uniqueLinks.slice(0, maxAmmountOfLinks); return linksToShow.map((linkData) => { + if (Array.isArray(linkData)) { + return; + } const {description, image, title, logo, publisher, url} = linkData; return ( linkData && ( diff --git a/src/types/onyx/ReportAction.ts b/src/types/onyx/ReportAction.ts index a0e90f4e9c34..60311e844054 100644 --- a/src/types/onyx/ReportAction.ts +++ b/src/types/onyx/ReportAction.ts @@ -144,9 +144,43 @@ type ReportActionBase = { isNewestReportAction?: boolean; }; +type ImageData = { + /** The height of the image. */ + height: number; + + /** The width of the image. */ + width: number; + + /** The URL of the image. */ + url: string; + + /** The type of the image. */ + type?: string; +}; + +type LinkMetadata = { + /** The URL of the link. */ + url?: string; + + /** A description of the link. */ + description?: string; + + /** The title of the link. */ + title?: string; + + /** The publisher of the link. */ + publisher?: string; + + /** The image associated with the link. */ + image?: ImageData; + + /** The provider logo associated with the link. */ + logo?: ImageData; +}; + type ReportAction = ReportActionBase & OriginalMessage; type ReportActions = Record; export default ReportAction; -export type {Message, ReportActions}; +export type {Message, ReportActions, LinkMetadata}; From 554727957f9e212678c2943045c63872a026175c Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Wed, 13 Dec 2023 10:24:49 +0100 Subject: [PATCH 29/98] fix typescript errors --- src/pages/home/report/LinkPreviewer.tsx | 2 +- src/types/onyx/ReportAction.ts | 37 ++----------------------- 2 files changed, 4 insertions(+), 35 deletions(-) diff --git a/src/pages/home/report/LinkPreviewer.tsx b/src/pages/home/report/LinkPreviewer.tsx index cdc7bfff23be..9b2bd2c45dc2 100644 --- a/src/pages/home/report/LinkPreviewer.tsx +++ b/src/pages/home/report/LinkPreviewer.tsx @@ -64,7 +64,7 @@ function LinkPreviewer({linkMetadata = [], maxAmountOfPreviews = -1}: LinkPrevie )} {description && {description}} - {image?.type && IMAGE_TYPES.includes(image.type) && ( + {image?.type && IMAGE_TYPES.includes(image.type) && image.width && image.height && ( ; From ce83d2b23c6f129546b2b43e27d32df41e3aa41d Mon Sep 17 00:00:00 2001 From: Sibtain Ali Date: Thu, 14 Dec 2023 06:03:27 +0500 Subject: [PATCH 30/98] feat: add types for report fields --- src/ONYXKEYS.ts | 4 ++++ src/types/onyx/PolicyReportField.ts | 22 ++++++++++++++++++++++ src/types/onyx/RecentlyUsedReportFields.ts | 3 +++ src/types/onyx/Report.ts | 3 +++ src/types/onyx/index.ts | 4 ++++ 5 files changed, 36 insertions(+) create mode 100644 src/types/onyx/PolicyReportField.ts create mode 100644 src/types/onyx/RecentlyUsedReportFields.ts diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 0cc7934ad007..76de820aedd6 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -254,6 +254,8 @@ const ONYXKEYS = { POLICY_RECENTLY_USED_CATEGORIES: 'policyRecentlyUsedCategories_', POLICY_TAGS: 'policyTags_', POLICY_RECENTLY_USED_TAGS: 'policyRecentlyUsedTags_', + POLICY_REPORT_FIELDS: 'policyReportFields_', + POLICY_RECENTLY_USED_REPORT_FIELDS: 'policyRecentlyUsedReportFields_', WORKSPACE_INVITE_MEMBERS_DRAFT: 'workspaceInviteMembersDraft_', WORKSPACE_INVITE_MESSAGE_DRAFT: 'workspaceInviteMessageDraft_', REPORT: 'report_', @@ -443,6 +445,8 @@ type OnyxValues = { [ONYXKEYS.COLLECTION.POLICY_MEMBERS]: OnyxTypes.PolicyMembers; [ONYXKEYS.COLLECTION.POLICY_MEMBERS_DRAFTS]: OnyxTypes.PolicyMember; [ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES]: OnyxTypes.RecentlyUsedCategories; + [ONYXKEYS.COLLECTION.POLICY_REPORT_FIELDS]: OnyxTypes.PolicyReportField; + [ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_REPORT_FIELDS]: OnyxTypes.RecentlyUsedReportFields; [ONYXKEYS.COLLECTION.DEPRECATED_POLICY_MEMBER_LIST]: OnyxTypes.PolicyMembers; [ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MEMBERS_DRAFT]: Record; [ONYXKEYS.COLLECTION.REPORT]: OnyxTypes.Report; diff --git a/src/types/onyx/PolicyReportField.ts b/src/types/onyx/PolicyReportField.ts new file mode 100644 index 000000000000..855ca2d06870 --- /dev/null +++ b/src/types/onyx/PolicyReportField.ts @@ -0,0 +1,22 @@ +type PolicyReportFieldType = "text" | "date" | "dropdown"; + +type PolicyReportField = { + /** Name of the field */ + name: string; + + /** Default value assigned to the field */ + defaultValue: string; + + /** Unique id of the field */ + fieldId: string; + + /** Position at which the field should show up relative to the other fields */ + orderWeight: number; + + /** Type of report field */ + type: PolicyReportFieldType; +}; + +type PolicyReportFields = Record; +export default PolicyReportField; +export type {PolicyReportFields}; diff --git a/src/types/onyx/RecentlyUsedReportFields.ts b/src/types/onyx/RecentlyUsedReportFields.ts new file mode 100644 index 000000000000..2b3d046c8316 --- /dev/null +++ b/src/types/onyx/RecentlyUsedReportFields.ts @@ -0,0 +1,3 @@ +type RecentlyUsedReportFields = Record; + +export default RecentlyUsedReportFields; diff --git a/src/types/onyx/Report.ts b/src/types/onyx/Report.ts index 25c544f6f5c3..bad6b6a942ea 100644 --- a/src/types/onyx/Report.ts +++ b/src/types/onyx/Report.ts @@ -157,6 +157,9 @@ type Report = { text?: string; privateNotes?: Record; isLoadingPrivateNotes?: boolean; + + /** If the report contains reportFields, save the field id and its value */ + reportFields?: Record, }; export default Report; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 110bdb024a8c..d105a21c6144 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -54,6 +54,8 @@ import WalletOnfido from './WalletOnfido'; import WalletStatement from './WalletStatement'; import WalletTerms from './WalletTerms'; import WalletTransfer from './WalletTransfer'; +import PolicyReportField from './PolicyReportField'; +import RecentlyUsedReportFields from './RecentlyUsedReportFields'; export type { Account, @@ -124,4 +126,6 @@ export type { WalletTerms, WalletTransfer, ReportUserIsTyping, + PolicyReportField, + RecentlyUsedReportFields, }; From b9d80d156a426170fa9dd9a99505b825c19b362d Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 14 Dec 2023 12:42:14 +0700 Subject: [PATCH 31/98] fix infinite loading in iou report --- src/pages/home/ReportScreen.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index 9d9330b9c720..71d16c118a7c 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -326,7 +326,7 @@ function ReportScreen({ !onyxReportID && prevReport.statusNum === CONST.REPORT.STATUS.OPEN && (report.statusNum === CONST.REPORT.STATUS.CLOSED || (!report.statusNum && !prevReport.parentReportID && prevReport.chatType === CONST.REPORT.CHAT_TYPE.POLICY_ROOM))) || - (ReportUtils.isMoneyRequest(prevReport) && _.isEqual(report, defaultProps.report)) + ((ReportUtils.isMoneyRequest(prevReport.reportID) || ReportUtils.isMoneyRequestReport(prevReport.reportID)) && _.isEqual(report, defaultProps.report)) ) { Navigation.dismissModal(); if (Navigation.getTopmostReportId() === prevOnyxReportID) { From 4a4317e1b5f4759ac573efc8c35ce95ea186df96 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 14 Dec 2023 16:41:46 +0700 Subject: [PATCH 32/98] fix issue --- src/pages/home/ReportScreen.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index 71d16c118a7c..8889a59f6ab2 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -316,6 +316,7 @@ function ReportScreen({ const onyxReportID = report.reportID; const prevOnyxReportID = prevReport.reportID; const routeReportID = getReportID(route); + // Navigate to the Concierge chat if the room was removed from another device (e.g. user leaving a room or removed from a room) if ( // non-optimistic case @@ -326,7 +327,7 @@ function ReportScreen({ !onyxReportID && prevReport.statusNum === CONST.REPORT.STATUS.OPEN && (report.statusNum === CONST.REPORT.STATUS.CLOSED || (!report.statusNum && !prevReport.parentReportID && prevReport.chatType === CONST.REPORT.CHAT_TYPE.POLICY_ROOM))) || - ((ReportUtils.isMoneyRequest(prevReport.reportID) || ReportUtils.isMoneyRequestReport(prevReport.reportID)) && _.isEqual(report, defaultProps.report)) + ((ReportUtils.isMoneyRequest(prevReport) || ReportUtils.isMoneyRequestReport(prevReport)) && _.isEqual(report, defaultProps.report)) ) { Navigation.dismissModal(); if (Navigation.getTopmostReportId() === prevOnyxReportID) { @@ -351,7 +352,19 @@ function ReportScreen({ fetchReportIfNeeded(); ComposerActions.setShouldShowComposeInput(true); - }, [route, report, errors, fetchReportIfNeeded, prevReport.reportID, prevUserLeavingStatus, userLeavingStatus, prevReport.statusNum, prevReport.parentReportID, prevReport.chatType]); + }, [ + route, + report, + errors, + fetchReportIfNeeded, + prevReport.reportID, + prevUserLeavingStatus, + userLeavingStatus, + prevReport.statusNum, + prevReport.parentReportID, + prevReport.chatType, + prevReport, + ]); useEffect(() => { if (!ReportUtils.isValidReportIDFromPath(reportID)) { From ed74e29cf6b0be7b31ae6365dfaf7c3d81d42d39 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Thu, 14 Dec 2023 15:14:05 +0100 Subject: [PATCH 33/98] fix PR comments --- src/pages/home/report/LinkPreviewer.tsx | 92 ++++++++++++------------- 1 file changed, 45 insertions(+), 47 deletions(-) diff --git a/src/pages/home/report/LinkPreviewer.tsx b/src/pages/home/report/LinkPreviewer.tsx index 9b2bd2c45dc2..cd5895c062dc 100644 --- a/src/pages/home/report/LinkPreviewer.tsx +++ b/src/pages/home/report/LinkPreviewer.tsx @@ -24,64 +24,62 @@ function LinkPreviewer({linkMetadata = [], maxAmountOfPreviews = -1}: LinkPrevie const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); - const uniqueLinks = linkMetadata.filter((link, index, self) => self.findIndex((t) => t.url === link.url) === index); + const uniqueLinks = linkMetadata.filter((link, index) => linkMetadata.findIndex(({url}) => url === link.url) === index); const maxAmmountOfLinks = maxAmountOfPreviews >= 0 ? Math.min(maxAmountOfPreviews, linkMetadata.length) : linkMetadata.length; const linksToShow = uniqueLinks.slice(0, maxAmmountOfLinks); return linksToShow.map((linkData) => { - if (Array.isArray(linkData)) { + if (!linkData && Array.isArray(linkData)) { return; } const {description, image, title, logo, publisher, url} = linkData; return ( - linkData && ( - - - {logo && ( - - )} - {publisher && ( - - {publisher} - - )} - - {title && url && ( - - {title} - - )} - {description && {description}} - {image?.type && IMAGE_TYPES.includes(image.type) && image.width && image.height && ( + + + {logo && ( )} + {publisher && ( + + {publisher} + + )} - ) + {title && url && ( + + {title} + + )} + {description && {description}} + {image?.type && IMAGE_TYPES.includes(image.type) && image.width && image.height && ( + + )} + ); }); } From e41e5750b38575aeff6a59b375da90c5f59be9af Mon Sep 17 00:00:00 2001 From: Sibtain Ali Date: Fri, 15 Dec 2023 11:22:17 +0500 Subject: [PATCH 34/98] feat: add more fields --- src/types/onyx/PolicyReportField.ts | 10 ++++++++-- src/types/onyx/Report.ts | 2 +- src/types/onyx/index.ts | 4 ++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/types/onyx/PolicyReportField.ts b/src/types/onyx/PolicyReportField.ts index 855ca2d06870..04876ea690b5 100644 --- a/src/types/onyx/PolicyReportField.ts +++ b/src/types/onyx/PolicyReportField.ts @@ -1,4 +1,4 @@ -type PolicyReportFieldType = "text" | "date" | "dropdown"; +type PolicyReportFieldType = 'text' | 'date' | 'dropdown' | 'formula'; type PolicyReportField = { /** Name of the field */ @@ -8,13 +8,19 @@ type PolicyReportField = { defaultValue: string; /** Unique id of the field */ - fieldId: string; + fieldID: string; /** Position at which the field should show up relative to the other fields */ orderWeight: number; /** Type of report field */ type: PolicyReportFieldType; + + /** Tells if the field is required or not */ + required: boolean; + + /** Options to select from if field is of type dropdown */ + options: string[]; }; type PolicyReportFields = Record; diff --git a/src/types/onyx/Report.ts b/src/types/onyx/Report.ts index bad6b6a942ea..509f27f9e95d 100644 --- a/src/types/onyx/Report.ts +++ b/src/types/onyx/Report.ts @@ -159,7 +159,7 @@ type Report = { isLoadingPrivateNotes?: boolean; /** If the report contains reportFields, save the field id and its value */ - reportFields?: Record, + reportFields?: Record; }; export default Report; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index d105a21c6144..c76dbe72192e 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -25,9 +25,11 @@ import PlaidData from './PlaidData'; import Policy from './Policy'; import PolicyCategory, {PolicyCategories} from './PolicyCategory'; import PolicyMember, {PolicyMembers} from './PolicyMember'; +import PolicyReportField from './PolicyReportField'; import PolicyTag, {PolicyTags} from './PolicyTag'; import PrivatePersonalDetails from './PrivatePersonalDetails'; import RecentlyUsedCategories from './RecentlyUsedCategories'; +import RecentlyUsedReportFields from './RecentlyUsedReportFields'; import RecentlyUsedTags from './RecentlyUsedTags'; import RecentWaypoint from './RecentWaypoint'; import ReimbursementAccount from './ReimbursementAccount'; @@ -54,8 +56,6 @@ import WalletOnfido from './WalletOnfido'; import WalletStatement from './WalletStatement'; import WalletTerms from './WalletTerms'; import WalletTransfer from './WalletTransfer'; -import PolicyReportField from './PolicyReportField'; -import RecentlyUsedReportFields from './RecentlyUsedReportFields'; export type { Account, From a40c9f891bff6913c98a9ca64753c21fb49193a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ska=C5=82ka?= Date: Fri, 15 Dec 2023 09:23:10 +0100 Subject: [PATCH 35/98] Add temporary video tag handling --- src/components/HTMLEngineProvider/HTMLRenderers/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/index.js b/src/components/HTMLEngineProvider/HTMLRenderers/index.js index 69f8eeac798e..13e410db8e3d 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/index.js +++ b/src/components/HTMLEngineProvider/HTMLRenderers/index.js @@ -14,6 +14,7 @@ export default { a: AnchorRenderer, code: CodeRenderer, img: ImageRenderer, + video: AnchorRenderer, // temporary until we have a video player component // Custom tag renderers edited: EditedRenderer, From bddf4d835e3a5104e331b4e6e36b8368ef1351a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ska=C5=82ka?= Date: Fri, 15 Dec 2023 10:42:57 +0100 Subject: [PATCH 36/98] Update BaseHTMLProvider --- src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js index 86ddf0a52bb3..58d7ae48647c 100755 --- a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js +++ b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js @@ -64,6 +64,10 @@ function BaseHTMLEngineProvider(props) { tagName: 'next-steps', mixedUAStyles: {...styles.textLabelSupporting}, }), + video: defaultHTMLElementModels.div.extend({ + tagName: 'video', + mixedUAStyles: {whiteSpace: 'pre'}, + }), }), [styles.colorMuted, styles.formError, styles.mb0, styles.textLabelSupporting], ); @@ -71,7 +75,6 @@ function BaseHTMLEngineProvider(props) { // We need to memoize this prop to make it referentially stable. const defaultTextProps = useMemo(() => ({selectable: props.textSelectable, allowFontScaling: false, textBreakStrategy: 'simple'}), [props.textSelectable]); const defaultViewProps = {style: [styles.alignItemsStart, styles.userSelectText]}; - return ( Date: Fri, 15 Dec 2023 11:40:26 +0100 Subject: [PATCH 37/98] Fix getting href for video tags in AnchorRenderer --- .../HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js b/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js index 8fd907470c19..c1cd5d6839a2 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js +++ b/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js @@ -20,7 +20,7 @@ function AnchorRenderer(props) { const isAttachment = Boolean(htmlAttribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE]); const displayName = lodashGet(props.tnode, 'domNode.children[0].data', ''); const parentStyle = lodashGet(props.tnode, 'parent.styles.nativeTextRet', {}); - const attrHref = htmlAttribs.href || ''; + const attrHref = htmlAttribs.href || htmlAttribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE] || ''; const internalNewExpensifyPath = Link.getInternalNewExpensifyPath(attrHref); const internalExpensifyPath = Link.getInternalExpensifyPath(attrHref); From 4d11558f9ec2c77c9aa6a4955cb09d5e880f5822 Mon Sep 17 00:00:00 2001 From: Maciej Dobosz Date: Fri, 15 Dec 2023 13:36:03 +0100 Subject: [PATCH 38/98] Only ask for location permission once --- src/components/MapView/MapView.tsx | 6 ++++++ src/components/MapView/MapView.website.tsx | 8 +++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/components/MapView/MapView.tsx b/src/components/MapView/MapView.tsx index 7b3d73479dde..f7a15ba10d2c 100644 --- a/src/components/MapView/MapView.tsx +++ b/src/components/MapView/MapView.tsx @@ -29,6 +29,7 @@ const MapView = forwardRef( const [isIdle, setIsIdle] = useState(false); const [currentPosition, setCurrentPosition] = useState(cachedUserLocation); const [userInteractedWithMap, setUserInteractedWithMap] = useState(false); + const hasAskedForLocationPermission = useRef(false); useFocusEffect( useCallback(() => { @@ -36,6 +37,11 @@ const MapView = forwardRef( return; } + if (hasAskedForLocationPermission.current) { + return; + } + + hasAskedForLocationPermission.current = true; getCurrentPosition( (params) => { const currentCoords = {longitude: params.coords.longitude, latitude: params.coords.latitude}; diff --git a/src/components/MapView/MapView.website.tsx b/src/components/MapView/MapView.website.tsx index 7910d7f93a29..851fa80506f6 100644 --- a/src/components/MapView/MapView.website.tsx +++ b/src/components/MapView/MapView.website.tsx @@ -5,7 +5,7 @@ import {useFocusEffect} from '@react-navigation/native'; import mapboxgl from 'mapbox-gl'; import 'mapbox-gl/dist/mapbox-gl.css'; -import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useState} from 'react'; +import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; import Map, {MapRef, Marker} from 'react-map-gl'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; @@ -52,6 +52,7 @@ const MapView = forwardRef( const [userInteractedWithMap, setUserInteractedWithMap] = useState(false); const [shouldResetBoundaries, setShouldResetBoundaries] = useState(false); const setRef = useCallback((newRef: MapRef | null) => setMapRef(newRef), []); + const hasAskedForLocationPermission = useRef(false); useFocusEffect( useCallback(() => { @@ -59,6 +60,11 @@ const MapView = forwardRef( return; } + if (hasAskedForLocationPermission.current) { + return; + } + + hasAskedForLocationPermission.current = true; getCurrentPosition( (params) => { const currentCoords = {longitude: params.coords.longitude, latitude: params.coords.latitude}; From 94faf791924005178e8ea255de3caec0b048c6a6 Mon Sep 17 00:00:00 2001 From: Maciej Dobosz Date: Fri, 15 Dec 2023 13:45:55 +0100 Subject: [PATCH 39/98] Get user location from cache on every focus --- src/components/MapView/MapView.tsx | 32 +++++++++++----------- src/components/MapView/MapView.website.tsx | 31 +++++++++++---------- 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/src/components/MapView/MapView.tsx b/src/components/MapView/MapView.tsx index f7a15ba10d2c..ac1af8d4d727 100644 --- a/src/components/MapView/MapView.tsx +++ b/src/components/MapView/MapView.tsx @@ -31,6 +31,15 @@ const MapView = forwardRef( const [userInteractedWithMap, setUserInteractedWithMap] = useState(false); const hasAskedForLocationPermission = useRef(false); + const getUserLocationFromCache = useCallback(() => { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + if (cachedUserLocation || !initialState) { + return; + } + + setCurrentPosition({longitude: initialState.location[0], latitude: initialState.location[1]}); + }, [cachedUserLocation, initialState]); + useFocusEffect( useCallback(() => { if (isOffline) { @@ -38,26 +47,17 @@ const MapView = forwardRef( } if (hasAskedForLocationPermission.current) { + getUserLocationFromCache(); return; } hasAskedForLocationPermission.current = true; - getCurrentPosition( - (params) => { - const currentCoords = {longitude: params.coords.longitude, latitude: params.coords.latitude}; - setCurrentPosition(currentCoords); - setUserLocation(currentCoords); - }, - () => { - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - if (cachedUserLocation || !initialState) { - return; - } - - setCurrentPosition({longitude: initialState.location[0], latitude: initialState.location[1]}); - }, - ); - }, [cachedUserLocation, initialState, isOffline]), + getCurrentPosition((params) => { + const currentCoords = {longitude: params.coords.longitude, latitude: params.coords.latitude}; + setCurrentPosition(currentCoords); + setUserLocation(currentCoords); + }, getUserLocationFromCache); + }, [getUserLocationFromCache, isOffline]), ); // Determines if map can be panned to user's detected diff --git a/src/components/MapView/MapView.website.tsx b/src/components/MapView/MapView.website.tsx index 851fa80506f6..81ad13696778 100644 --- a/src/components/MapView/MapView.website.tsx +++ b/src/components/MapView/MapView.website.tsx @@ -54,6 +54,15 @@ const MapView = forwardRef( const setRef = useCallback((newRef: MapRef | null) => setMapRef(newRef), []); const hasAskedForLocationPermission = useRef(false); + const getUserLocationFromCache = useCallback(() => { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + if (cachedUserLocation || !initialState) { + return; + } + + setCurrentPosition({longitude: initialState.location[0], latitude: initialState.location[1]}); + }, [cachedUserLocation, initialState]); + useFocusEffect( useCallback(() => { if (isOffline) { @@ -61,25 +70,17 @@ const MapView = forwardRef( } if (hasAskedForLocationPermission.current) { + getUserLocationFromCache(); return; } hasAskedForLocationPermission.current = true; - getCurrentPosition( - (params) => { - const currentCoords = {longitude: params.coords.longitude, latitude: params.coords.latitude}; - setCurrentPosition(currentCoords); - setUserLocation(currentCoords); - }, - () => { - if (cachedUserLocation) { - return; - } - - setCurrentPosition({longitude: initialState.location[0], latitude: initialState.location[1]}); - }, - ); - }, [cachedUserLocation, isOffline, initialState.location]), + getCurrentPosition((params) => { + const currentCoords = {longitude: params.coords.longitude, latitude: params.coords.latitude}; + setCurrentPosition(currentCoords); + setUserLocation(currentCoords); + }, getUserLocationFromCache); + }, [getUserLocationFromCache, isOffline]), ); // Determines if map can be panned to user's detected From 443a9cafca94355e0fd0db1a3eb4cffbe8dce85a Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Fri, 15 Dec 2023 16:15:43 +0100 Subject: [PATCH 40/98] fix: apply requested changes --- src/pages/SearchPage.js | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index 68724b51e796..ad8e15287d4f 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -8,11 +8,11 @@ import OptionsSelector from '@components/OptionsSelector'; import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; +import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import Performance from '@libs/Performance'; import * as ReportUtils from '@libs/ReportUtils'; -import useThemeStyles from '@styles/useThemeStyles'; import * as Report from '@userActions/Report'; import Timing from '@userActions/Timing'; import CONST from '@src/CONST'; @@ -44,20 +44,19 @@ const defaultProps = { }; function SearchPage({betas, personalDetails, reports, isSearchingForReports}) { - // Data for initialization (runs only on the first render) - const { - recentReports: initialRecentReports, - personalDetails: initialPersonalDetails, - userToInvite: initialUserToInvite, - // Ignoring the rule because in this case we need the data only initially - // eslint-disable-next-line react-hooks/exhaustive-deps - } = useMemo(() => OptionsListUtils.getSearchOptions(reports, personalDetails, '', betas), []); - const [searchValue, setSearchValue] = useState(''); - const [searchOptions, setSearchOptions] = useState({ - recentReports: initialRecentReports, - personalDetails: initialPersonalDetails, - userToInvite: initialUserToInvite, + const [searchOptions, setSearchOptions] = useState(() => { + const { + recentReports: localRecentReports, + personalDetails: localPersonalDetails, + userToInvite: localUserToInvite, + } = OptionsListUtils.getSearchOptions(reports, personalDetails, '', betas); + + return { + recentReports: localRecentReports, + personalDetails: localPersonalDetails, + userToInvite: localUserToInvite, + }; }); const {isOffline} = useNetwork(); From a6cb72c58c160927b5928de7ca10d1da7d494b3d Mon Sep 17 00:00:00 2001 From: Fitsum Abebe Date: Sat, 16 Dec 2023 18:20:39 +0300 Subject: [PATCH 41/98] removed map view container max height --- src/styles/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/styles/index.ts b/src/styles/index.ts index 9e02335bde0d..712e91e70af0 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -3850,7 +3850,6 @@ const styles = (theme: ThemeColors) => mapViewContainer: { ...flex.flex1, minHeight: 300, - maxHeight: 500, }, mapView: { From 95c70d6ccb15d9f56ff2d9c266b9e578dc55416c Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 18 Dec 2023 13:11:47 +0700 Subject: [PATCH 42/98] fix: message header disappears when delete message in offline --- src/pages/home/report/ReportActionsList.js | 2 +- src/pages/home/report/ReportActionsListItemRenderer.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index e2ae7b947fcc..2c8362268ef8 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -383,7 +383,7 @@ function ReportActionsList({ index={index} report={report} linkedReportActionID={linkedReportActionID} - sortedReportActions={sortedReportActions} + displayAsGroup={ReportActionsUtils.isConsecutiveActionMadeByPreviousActor(sortedReportActions, index)} mostRecentIOUReportActionID={mostRecentIOUReportActionID} shouldHideThreadDividerLine={shouldHideThreadDividerLine} shouldDisplayNewMarker={shouldDisplayNewMarker(reportAction, index)} diff --git a/src/pages/home/report/ReportActionsListItemRenderer.js b/src/pages/home/report/ReportActionsListItemRenderer.js index 0e558541c69a..ba47e804de06 100644 --- a/src/pages/home/report/ReportActionsListItemRenderer.js +++ b/src/pages/home/report/ReportActionsListItemRenderer.js @@ -19,8 +19,8 @@ const propTypes = { /** Report for this action */ report: reportPropTypes.isRequired, - /** Sorted actions prepared for display */ - sortedReportActions: PropTypes.arrayOf(PropTypes.shape(reportActionPropTypes)).isRequired, + /** Should the comment have the appearance of being grouped with the previous comment? */ + displayAsGroup: PropTypes.bool.isRequired, /** The ID of the most recent IOU report action connected with the shown report */ mostRecentIOUReportActionID: PropTypes.string, @@ -44,7 +44,7 @@ function ReportActionsListItemRenderer({ reportAction, index, report, - sortedReportActions, + displayAsGroup, mostRecentIOUReportActionID, shouldHideThreadDividerLine, shouldDisplayNewMarker, @@ -68,7 +68,7 @@ function ReportActionsListItemRenderer({ report={report} action={reportAction} linkedReportActionID={linkedReportActionID} - displayAsGroup={ReportActionsUtils.isConsecutiveActionMadeByPreviousActor(sortedReportActions, index)} + displayAsGroup={displayAsGroup} shouldDisplayNewMarker={shouldDisplayNewMarker} shouldShowSubscriptAvatar={ (ReportUtils.isPolicyExpenseChat(report) || ReportUtils.isExpenseReport(report)) && From ac2860907f6c34e93301510b0b1bb41745341220 Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 18 Dec 2023 13:35:23 +0700 Subject: [PATCH 43/98] fix: loading shows even though not found in profile page --- src/pages/ProfilePage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/ProfilePage.js b/src/pages/ProfilePage.js index 3772b8127ef7..62c7ed75a9f0 100755 --- a/src/pages/ProfilePage.js +++ b/src/pages/ProfilePage.js @@ -125,7 +125,7 @@ function ProfilePage(props) { const isCurrentUser = props.session.accountID === accountID; const hasMinimumDetails = !_.isEmpty(details.avatar); - const isLoading = lodashGet(details, 'isLoading', false) || _.isEmpty(details) || props.isLoadingReportData; + const isLoading = lodashGet(details, 'isLoading', false) || _.isEmpty(details); // If the API returns an error for some reason there won't be any details and isLoading will get set to false, so we want to show a blocking screen const shouldShowBlockingView = !hasMinimumDetails && !isLoading; From 52183ad09e062fd419122a37cf707498e1c6ca21 Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 18 Dec 2023 14:13:07 +0700 Subject: [PATCH 44/98] fix lint --- src/pages/ProfilePage.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/pages/ProfilePage.js b/src/pages/ProfilePage.js index 62c7ed75a9f0..b50798f2d856 100755 --- a/src/pages/ProfilePage.js +++ b/src/pages/ProfilePage.js @@ -57,9 +57,6 @@ const propTypes = { /** Route params */ route: matchType.isRequired, - /** Indicates whether the app is loading initial data */ - isLoadingReportData: PropTypes.bool, - /** Session info for the currently logged in user. */ session: PropTypes.shape({ /** Currently logged in user accountID */ @@ -72,7 +69,6 @@ const propTypes = { const defaultProps = { // When opening someone else's profile (via deep link) before login, this is empty personalDetails: {}, - isLoadingReportData: true, session: { accountID: 0, }, @@ -288,9 +284,6 @@ export default compose( personalDetails: { key: ONYXKEYS.PERSONAL_DETAILS_LIST, }, - isLoadingReportData: { - key: ONYXKEYS.IS_LOADING_REPORT_DATA, - }, session: { key: ONYXKEYS.SESSION, }, From 80196fcefc867fc6ded66c8c8b03101d4cba13c7 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Mon, 18 Dec 2023 14:22:59 +0700 Subject: [PATCH 45/98] check if report empty --- src/pages/home/ReportScreen.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index 3f7e2fdfaf74..1974296c82e1 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -350,7 +350,7 @@ function ReportScreen({ !onyxReportID && prevReport.statusNum === CONST.REPORT.STATUS.OPEN && (report.statusNum === CONST.REPORT.STATUS.CLOSED || (!report.statusNum && !prevReport.parentReportID && prevReport.chatType === CONST.REPORT.CHAT_TYPE.POLICY_ROOM))) || - ((ReportUtils.isMoneyRequest(prevReport) || ReportUtils.isMoneyRequestReport(prevReport)) && _.isEqual(report, defaultProps.report)) + ((ReportUtils.isMoneyRequest(prevReport) || ReportUtils.isMoneyRequestReport(prevReport)) && _.isEmpty(report)) ) { Navigation.dismissModal(); if (Navigation.getTopmostReportId() === prevOnyxReportID) { From 23a9a01bb9e3f4ff6b73db07de690c270bf179a2 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Mon, 18 Dec 2023 12:54:28 +0100 Subject: [PATCH 46/98] fix comments --- src/pages/home/report/LinkPreviewer.tsx | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/pages/home/report/LinkPreviewer.tsx b/src/pages/home/report/LinkPreviewer.tsx index d0952c0365d9..dd6a2b9ab61e 100644 --- a/src/pages/home/report/LinkPreviewer.tsx +++ b/src/pages/home/report/LinkPreviewer.tsx @@ -20,11 +20,27 @@ type LinkPreviewerProps = { maxAmountOfPreviews?: number; }; +function filterNonUniqueLinks(linkMetadata: LinkMetadata[]): LinkMetadata[] { + const map = new Map(); + const result: LinkMetadata[] = []; + + linkMetadata.forEach((item) => { + if (!item.url || map.has(item.url)) { + return; + } + + map.set(item.url, item.url); + result.push(item); + }); + + return result; +} + function LinkPreviewer({linkMetadata = [], maxAmountOfPreviews = -1}: LinkPreviewerProps) { const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); - const uniqueLinks = linkMetadata.filter((link, index) => linkMetadata.findIndex(({url}) => url === link.url) === index); + const uniqueLinks = filterNonUniqueLinks(linkMetadata); const maxAmmountOfLinks = maxAmountOfPreviews >= 0 ? Math.min(maxAmountOfPreviews, linkMetadata.length) : linkMetadata.length; const linksToShow = uniqueLinks.slice(0, maxAmmountOfLinks); return linksToShow.map((linkData) => { From 84a862be15d875b50212d787c5586cb610a97c79 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Mon, 18 Dec 2023 12:55:42 +0100 Subject: [PATCH 47/98] fix naming --- src/pages/home/report/LinkPreviewer.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/home/report/LinkPreviewer.tsx b/src/pages/home/report/LinkPreviewer.tsx index dd6a2b9ab61e..209096d2fa15 100644 --- a/src/pages/home/report/LinkPreviewer.tsx +++ b/src/pages/home/report/LinkPreviewer.tsx @@ -21,15 +21,15 @@ type LinkPreviewerProps = { }; function filterNonUniqueLinks(linkMetadata: LinkMetadata[]): LinkMetadata[] { - const map = new Map(); + const linksMap = new Map(); const result: LinkMetadata[] = []; linkMetadata.forEach((item) => { - if (!item.url || map.has(item.url)) { + if (!item.url || linksMap.has(item.url)) { return; } - map.set(item.url, item.url); + linksMap.set(item.url, item.url); result.push(item); }); From 766e6b6dec0e9ffd8cebe0c9c2dcfa9d6fe41e06 Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Mon, 18 Dec 2023 13:06:22 +0100 Subject: [PATCH 48/98] revert logic --- src/pages/home/report/withReportAndReportActionOrNotFound.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/home/report/withReportAndReportActionOrNotFound.tsx b/src/pages/home/report/withReportAndReportActionOrNotFound.tsx index 7fb222334a8a..9ad98a84bd1a 100644 --- a/src/pages/home/report/withReportAndReportActionOrNotFound.tsx +++ b/src/pages/home/report/withReportAndReportActionOrNotFound.tsx @@ -13,7 +13,7 @@ import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; import * as Report from '@userActions/Report'; import ONYXKEYS from '@src/ONYXKEYS'; import * as OnyxTypes from '@src/types/onyx'; -import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import {isEmptyObject, isNotEmptyObject} from '@src/types/utils/EmptyObject'; type OnyxProps = { /** The report currently being looked at */ @@ -58,7 +58,7 @@ export default function (WrappedComponent: // For small screen, we don't call openReport API when we go to a sub report page by deeplink // So we need to call openReport here for small screen useEffect(() => { - if (!props.isSmallScreenWidth || (isEmptyObject(props.report) && isEmptyObject(reportAction))) { + if (!props.isSmallScreenWidth || (isNotEmptyObject(props.report) && isNotEmptyObject(reportAction))) { return; } Report.openReport(props.route.params.reportID); From 6fc83d5c0a882f41027994c7dd4382859895ce9a Mon Sep 17 00:00:00 2001 From: Roji Philip Date: Mon, 18 Dec 2023 20:22:14 +0530 Subject: [PATCH 49/98] use capture groups for regex --- src/libs/UserUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/UserUtils.ts b/src/libs/UserUtils.ts index e95b62cc2437..ba19002345e7 100644 --- a/src/libs/UserUtils.ts +++ b/src/libs/UserUtils.ts @@ -105,8 +105,8 @@ function getDefaultAvatar(accountID = -1, avatarURL?: string): React.FC Date: Mon, 18 Dec 2023 15:31:33 -0500 Subject: [PATCH 50/98] move clearing notification logic to ReportScreen --- src/libs/actions/Report.ts | 3 --- src/pages/home/ReportScreen.js | 20 ++++++++++---------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index c3b1532b4d87..a13e1e34eea9 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -16,7 +16,6 @@ import * as Environment from '@libs/Environment/Environment'; import * as ErrorUtils from '@libs/ErrorUtils'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; -import clearReportNotifications from '@libs/Notification/clearReportNotifications'; import LocalNotification from '@libs/Notification/LocalNotification'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; @@ -475,8 +474,6 @@ function openReport( return; } - clearReportNotifications(reportID); - const optimisticReport = reportActionsExist(reportID) ? {} : { diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index 161b8aa8889d..6ad68af4577c 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -291,17 +291,17 @@ function ReportScreen({ [route], ); - // Clear notifications for the current report when the app is focused - useAppFocusEvent( - useCallback(() => { - // Check if this is the top-most ReportScreen since the Navigator preserves multiple at a time - if (!isTopMostReportId) { - return; - } + // Clear notifications for the current report when it's opened and re-focused + const clearNotifications = useCallback(() => { + // Check if this is the top-most ReportScreen since the Navigator preserves multiple at a time + if (!isTopMostReportId) { + return; + } - clearReportNotifications(report.reportID); - }, [report.reportID, isTopMostReportId]), - ); + clearReportNotifications(report.reportID); + }, [report.reportID, isTopMostReportId]); + useEffect(clearNotifications, [clearNotifications]); + useAppFocusEvent(clearNotifications); useEffect(() => { Timing.end(CONST.TIMING.CHAT_RENDER); From ffa77cb8752193d5979a58647dadaff8a6866f6c Mon Sep 17 00:00:00 2001 From: DylanDylann Date: Tue, 19 Dec 2023 11:08:18 +0700 Subject: [PATCH 51/98] fix focusing on item by tab key does not work --- src/components/OptionRow.js | 3 +++ src/components/OptionsList/BaseOptionsList.js | 1 + src/components/OptionsSelector/BaseOptionsSelector.js | 5 +++-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/OptionRow.js b/src/components/OptionRow.js index b6628d7444ab..0c1e5161f496 100644 --- a/src/components/OptionRow.js +++ b/src/components/OptionRow.js @@ -77,6 +77,7 @@ const propTypes = { isMultilineSupported: PropTypes.bool, style: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]), + keyForList: PropTypes.string, ...withLocalizePropTypes, }; @@ -99,6 +100,7 @@ const defaultProps = { shouldHaveOptionSeparator: false, shouldDisableRowInnerPadding: false, shouldPreventDefaultFocusOnSelectRow: false, + keyForList: undefined, }; function OptionRow(props) { @@ -163,6 +165,7 @@ function OptionRow(props) { {(hovered) => ( (pressableRef.current = el)} onPress={(e) => { if (!props.onSelectRow) { diff --git a/src/components/OptionsList/BaseOptionsList.js b/src/components/OptionsList/BaseOptionsList.js index d22df00bd0b3..bcd990a6909f 100644 --- a/src/components/OptionsList/BaseOptionsList.js +++ b/src/components/OptionsList/BaseOptionsList.js @@ -201,6 +201,7 @@ function BaseOptionsList({ return ( option.keyForList === focusedItemKey) : this.state.allOptions[this.state.focusedIndex]; if (!focusedOption || !this.props.isFocused) { return; From f2b74e83a7769a3a5b69f0391abfb3b87b92cba3 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Tue, 19 Dec 2023 15:48:02 +0800 Subject: [PATCH 52/98] hide add receipt placeholder if the user can't add it --- src/components/ReportActionItem/MoneyRequestView.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js index 817c88d456db..43acfc1b5960 100644 --- a/src/components/ReportActionItem/MoneyRequestView.js +++ b/src/components/ReportActionItem/MoneyRequestView.js @@ -118,6 +118,7 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate const isCancelled = moneyRequestReport && moneyRequestReport.isCancelledIOU; const canEdit = ReportUtils.canEditMoneyRequest(parentReportAction); const canEditAmount = canEdit && !isSettled && !isCardTransaction; + const canEditReceipt = ReportUtils.canEditFieldOfMoneyRequest(parentReportAction, moneyRequestReport.reportID, CONST.EDIT_REQUEST_FIELD.RECEIPT); // A flag for verifying that the current report is a sub-report of a workspace chat const isPolicyExpenseChat = useMemo(() => ReportUtils.isPolicyExpenseChat(ReportUtils.getRootParentReport(report)), [report]); @@ -192,7 +193,7 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate )} - {!hasReceipt && canEdit && !isSettled && canUseViolations && ( + {!hasReceipt && canEditReceipt && !isSettled && canUseViolations && ( Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.RECEIPT))} From 56d4762fc5f70f34f553bd7e4b3b759f96a2a8a4 Mon Sep 17 00:00:00 2001 From: Sibtain Ali Date: Tue, 19 Dec 2023 15:31:21 +0500 Subject: [PATCH 53/98] fix: use same types as backend --- src/types/onyx/PolicyReportField.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types/onyx/PolicyReportField.ts b/src/types/onyx/PolicyReportField.ts index 04876ea690b5..6a3be23c0a0b 100644 --- a/src/types/onyx/PolicyReportField.ts +++ b/src/types/onyx/PolicyReportField.ts @@ -17,10 +17,10 @@ type PolicyReportField = { type: PolicyReportFieldType; /** Tells if the field is required or not */ - required: boolean; + deletable: boolean; /** Options to select from if field is of type dropdown */ - options: string[]; + values: string[]; }; type PolicyReportFields = Record; From 7043a17797660db9bfebf895c21508a66dd3d95d Mon Sep 17 00:00:00 2001 From: Roji Philip Date: Tue, 19 Dec 2023 17:35:38 +0530 Subject: [PATCH 54/98] unit test for default avatar based on url --- tests/unit/UserUtilsTest.js | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tests/unit/UserUtilsTest.js diff --git a/tests/unit/UserUtilsTest.js b/tests/unit/UserUtilsTest.js new file mode 100644 index 000000000000..f0f20fc6d4cb --- /dev/null +++ b/tests/unit/UserUtilsTest.js @@ -0,0 +1,9 @@ +import * as UserUtils from '../../src/libs/UserUtils'; + +describe('UserUtils', () => { + it('should return the default avatar from the avatar url', () => { + const avatarURL = 'https://d2k5nsl2zxldvw.cloudfront.net/images/avatars/default-avatar_7.png'; + const defaultAvatar = UserUtils.getDefaultAvatar(1, avatarURL); + expect(typeof defaultAvatar).toBe('function'); + }); +}); From 2497dc561c10cc8fba8e488b748b85151e02211b Mon Sep 17 00:00:00 2001 From: hurali97 Date: Tue, 19 Dec 2023 17:54:00 +0500 Subject: [PATCH 55/98] test: add support for transitionEnd event --- tests/ui/UnreadIndicatorsTest.js | 89 +++++++++++++++++++++++++++----- 1 file changed, 76 insertions(+), 13 deletions(-) diff --git a/tests/ui/UnreadIndicatorsTest.js b/tests/ui/UnreadIndicatorsTest.js index 93bb2cb94d30..8874e549d59e 100644 --- a/tests/ui/UnreadIndicatorsTest.js +++ b/tests/ui/UnreadIndicatorsTest.js @@ -1,4 +1,4 @@ -import {fireEvent, render, screen, waitFor} from '@testing-library/react-native'; +import {act, fireEvent, render, screen, waitFor} from '@testing-library/react-native'; import {addSeconds, format, subMinutes, subSeconds} from 'date-fns'; import {utcToZonedTime} from 'date-fns-tz'; import lodashGet from 'lodash/get'; @@ -31,6 +31,61 @@ jest.setTimeout(30000); jest.mock('../../src/libs/Notification/LocalNotification'); jest.mock('../../src/components/Icon/Expensicons'); +/** + * We need to keep track of the transitionEnd callback so we can trigger it in our tests + */ +let transitionEndCB; + +/** + * This is a helper function to create a mock for the addListener function of the react-navigation library. + * The reason we need this is because we need to trigger the transitionEnd event in our tests to simulate + * the transitionEnd event that is triggered when the screen transition animation is completed. + * + * P.S: This can't be moved to a utils file because Jest wants any external function to stay in the scope. + * + * @returns {Object} An object with two functions: triggerTransitionEnd and addListener + */ +const createAddListenerMock = () => { + const transitionEndListeners = []; + const triggerTransitionEnd = () => { + transitionEndListeners.forEach((transitionEndListener) => transitionEndListener()); + }; + + const addListener = jest.fn().mockImplementation((listener, callback) => { + if (listener === 'transitionEnd') { + transitionEndListeners.push(callback); + } + return () => { + // eslint-disable-next-line rulesdir/prefer-underscore-method + transitionEndListeners.filter((cb) => cb !== callback); + }; + }); + + return {triggerTransitionEnd, addListener}; +}; + +jest.mock('@react-navigation/native', () => { + const actualNav = jest.requireActual('@react-navigation/native'); + const {triggerTransitionEnd, addListener} = createAddListenerMock(); + transitionEndCB = triggerTransitionEnd; + const useNavigation = () => ({ + navigate: jest.fn(), + ...actualNav.useNavigation, + getState: () => ({ + routes: [], + }), + addListener, + }); + + return { + ...actualNav, + useNavigation, + getState: () => ({ + routes: [], + }), + }; +}); + beforeAll(() => { // In this test, we are generically mocking the responses of all API requests by mocking fetch() and having it // return 200. In other tests, we might mock HttpUtils.xhr() with a more specific mock data response (which means @@ -95,11 +150,11 @@ function navigateToSidebar() { * @param {Number} index * @return {Promise} */ -function navigateToSidebarOption(index) { +async function navigateToSidebarOption(index) { const hintText = Localize.translateLocal('accessibilityHints.navigatesToChat'); const optionRows = screen.queryAllByAccessibilityHint(hintText); fireEvent(optionRows[index], 'press'); - return waitForBatchedUpdates(); + await waitForBatchedUpdatesWithAct(); } /** @@ -136,19 +191,22 @@ function signInAndGetAppWithUnreadChat() { const loginForm = screen.queryAllByLabelText(hintText); expect(loginForm).toHaveLength(1); - return TestHelper.signInWithTestUser(USER_A_ACCOUNT_ID, USER_A_EMAIL, undefined, undefined, 'A'); + await act(async () => { + await TestHelper.signInWithTestUser(USER_A_ACCOUNT_ID, USER_A_EMAIL, undefined, undefined, 'A'); + }); + return waitForBatchedUpdatesWithAct(); }) .then(() => { User.subscribeToUserEvents(); return waitForBatchedUpdates(); }) - .then(() => { + .then(async () => { const TEN_MINUTES_AGO = subMinutes(new Date(), 10); reportAction3CreatedDate = format(addSeconds(TEN_MINUTES_AGO, 30), CONST.DATE.FNS_DB_FORMAT_STRING); reportAction9CreatedDate = format(addSeconds(TEN_MINUTES_AGO, 90), CONST.DATE.FNS_DB_FORMAT_STRING); // Simulate setting an unread report and personal details - Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, { + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, { reportID: REPORT_ID, reportName: CONST.REPORT.DEFAULT_REPORT_NAME, lastReadTime: reportAction3CreatedDate, @@ -158,7 +216,7 @@ function signInAndGetAppWithUnreadChat() { type: CONST.REPORT.TYPE.CHAT, }); const createdReportActionID = NumberUtils.rand64(); - Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${REPORT_ID}`, { + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${REPORT_ID}`, { [createdReportActionID]: { actionName: CONST.REPORT.ACTIONS.TYPE.CREATED, automatic: false, @@ -187,13 +245,13 @@ function signInAndGetAppWithUnreadChat() { 8: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 80), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '8'), 9: TestHelper.buildTestReportComment(reportAction9CreatedDate, USER_B_ACCOUNT_ID, '9'), }); - Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, { + await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, { [USER_B_ACCOUNT_ID]: TestHelper.buildPersonalDetails(USER_B_EMAIL, USER_B_ACCOUNT_ID, 'B'), }); // We manually setting the sidebar as loaded since the onLayout event does not fire in tests AppActions.setSidebarLoaded(true); - return waitForBatchedUpdates(); + return waitForBatchedUpdatesWithAct(); }); } @@ -226,7 +284,9 @@ describe('Unread Indicators', () => { return navigateToSidebarOption(0); }) - .then(() => { + .then(async () => { + await act(() => transitionEndCB && transitionEndCB()); + // That the report actions are visible along with the created action const welcomeMessageHintText = Localize.translateLocal('accessibilityHints.chatWelcomeMessage'); const createdAction = screen.queryByLabelText(welcomeMessageHintText); @@ -249,7 +309,8 @@ describe('Unread Indicators', () => { signInAndGetAppWithUnreadChat() // Navigate to the unread chat from the sidebar .then(() => navigateToSidebarOption(0)) - .then(() => { + .then(async () => { + await act(() => transitionEndCB && transitionEndCB()); // Verify the unread indicator is present const newMessageLineIndicatorHintText = Localize.translateLocal('accessibilityHints.newMessageLineIndicator'); const unreadIndicator = screen.queryAllByLabelText(newMessageLineIndicatorHintText); @@ -373,7 +434,8 @@ describe('Unread Indicators', () => { return navigateToSidebarOption(0); }) .then(waitForBatchedUpdates) - .then(() => { + .then(async () => { + await act(() => transitionEndCB && transitionEndCB()); // Verify that report we navigated to appears in a "read" state while the original unread report still shows as unread const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNameTexts = screen.queryAllByLabelText(hintText); @@ -449,7 +511,8 @@ describe('Unread Indicators', () => { // Navigate to the report and verify the indicator is present return navigateToSidebarOption(0); }) - .then(() => { + .then(async () => { + await act(() => transitionEndCB && transitionEndCB()); const newMessageLineIndicatorHintText = Localize.translateLocal('accessibilityHints.newMessageLineIndicator'); const unreadIndicator = screen.queryAllByLabelText(newMessageLineIndicatorHintText); expect(unreadIndicator).toHaveLength(1); From cf4848a4c38883b9de8d0f89700c6ea9ed3ec73c Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 11 Sep 2023 17:54:37 +0100 Subject: [PATCH 56/98] add monochrome app icon for Android theming --- .../res/mipmap-anydpi-v26/ic_launcher.xml | 1 + .../main/res/drawable/ic_launcher_monochrome.png | Bin 0 -> 1309 bytes .../main/res/mipmap-anydpi-v26/ic_launcher.xml | 1 + 3 files changed, 2 insertions(+) create mode 100644 android/app/src/main/res/drawable/ic_launcher_monochrome.png diff --git a/android/app/src/development/res/mipmap-anydpi-v26/ic_launcher.xml b/android/app/src/development/res/mipmap-anydpi-v26/ic_launcher.xml index 7353dbd1fd82..1084c240820a 100644 --- a/android/app/src/development/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/android/app/src/development/res/mipmap-anydpi-v26/ic_launcher.xml @@ -2,4 +2,5 @@ + \ No newline at end of file diff --git a/android/app/src/main/res/drawable/ic_launcher_monochrome.png b/android/app/src/main/res/drawable/ic_launcher_monochrome.png new file mode 100644 index 0000000000000000000000000000000000000000..4eea447cd3cbd8e4724b325e93ee39c4dbdfa147 GIT binary patch literal 1309 zcmV+&1>*XNP)fFe@b~^1pc`D>2`CdFCZJ4EGXZ6SW&_FuDH8w_#7sc>$fuYI$_8Lz8%pZq%Jw;q zY`?$fIZr`J)7U4HWl4?!A|fIpA|fIpA|fIpA|j$8{=$5nmf_3zuMAiJAN9ZQD$4j) z$4wbu>z_Hu)JYi^W$enhE#tdFgxAV&Kgl%E-6VZCjwX3DKuFXj^lDO{*w=sCfubBM zQEiF40Es-Rousj&?}o-1ODD?(9h#U z*zvJDE$ryfOr2mnhbq~9lDET(dBh#7!gDsT?F6_ot`V0sjStK0oFi`Mgyw6?9pvzE z;#QZiOT;xj4#(rr-c(*;WnyYpN?qv;RpR!jn&nmTc)0s~3QB@Q_c_rX99c+8SF%F4 z-eJBxs^; zk9)pW5?(^CT^sbXL;Ud3(Ac3L554U>ItRI<@d^EUQ!o;;qTLyWjI1H0=!a`D{SqJr6X3->`3+2~{?-kBT=ncLV z9(#twIq27}AmthsoWf?i@6kRF^FnzJ``YypRV^NB%z0C@Fx9s_)+|d%l2j8ij1yDC zwm7Z}=+|6xR?Wg35_Wo$@(HD;yo?t(E@=o#En%8X;#Z-3O&2&wHS9B#SRccJH6_dozCM*}q zA7MS72<1~85(bf04~^|D&S3)6L4I#)w9mt&Pf~{E^FnzJMcUo^4`_emVY!#}LKKH_ zAB+OycJ8@Q{uT+7rWeLGi&oPsT8m{I?AEjVLAyx|=Nq~3nEEQ%gj@>cIh1ECEXEQ) z2<74Oqa_YiImk_M(#>I`YpwYP!=&6KZecvjJcSK(+w@cu<~m+)gBj_nWkYsMstMae zu3ZiWtmeZ)=i`m=n8z|YAQ|MOgl#Wi!1`OYrB7O3##6*=rfIAdJ)V1w_H(bIjQ=rH z9_>xWkn5aNBVBpvF>`Gjw!!>4+&@kyds$H*p((q@!Zxbr%6NyW!?MA`Om!S0O2UiDx8-`zPz6gR!RN4x%&2qYqn5o4AL*L;pRf zbKG&N#5u&>LSzSe@VkHycUT6U*`G!i#9np=oa1cz#89V2l~qBtruP=@IyFvIbXiBjUAo4i}- z-ri~w))SbPJgT0bL$!Xt8C-S;&nxPmH~4)^;3kNOh=_=Yh=_=Yh=_=Yh*shU?tEd# TATL`S00000NkvXXu0mjf7Bgn7 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index 7353dbd1fd82..1084c240820a 100644 --- a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -2,4 +2,5 @@ + \ No newline at end of file From ac2dd0673e90bb640af648bd143d8d4ab9d7f220 Mon Sep 17 00:00:00 2001 From: maddylewis <38016013+maddylewis@users.noreply.github.com> Date: Tue, 19 Dec 2023 09:13:25 -0500 Subject: [PATCH 57/98] Delete docs/articles/expensify-classic/manage-employees-and-report-approvals/Add-Members-to-your-Workspace.md empty page / deleting --- .../Add-Members-to-your-Workspace.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 docs/articles/expensify-classic/manage-employees-and-report-approvals/Add-Members-to-your-Workspace.md diff --git a/docs/articles/expensify-classic/manage-employees-and-report-approvals/Add-Members-to-your-Workspace.md b/docs/articles/expensify-classic/manage-employees-and-report-approvals/Add-Members-to-your-Workspace.md deleted file mode 100644 index f2978434959b..000000000000 --- a/docs/articles/expensify-classic/manage-employees-and-report-approvals/Add-Members-to-your-Workspace.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add Members to your Workspace -description: Coming Soon ---- -## Resource Coming Soon! From 0f1cdd12f419b9bf37b0ae4054f7a244b0f8ed2e Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 19 Dec 2023 16:30:51 +0000 Subject: [PATCH 58/98] update AdHoc to themed icon --- android/app/src/adhoc/res/mipmap-anydpi-v26/ic_launcher.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/android/app/src/adhoc/res/mipmap-anydpi-v26/ic_launcher.xml b/android/app/src/adhoc/res/mipmap-anydpi-v26/ic_launcher.xml index 7353dbd1fd82..1084c240820a 100644 --- a/android/app/src/adhoc/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/android/app/src/adhoc/res/mipmap-anydpi-v26/ic_launcher.xml @@ -2,4 +2,5 @@ + \ No newline at end of file From a459d902543d6e260f88acae99a711775ed71ab7 Mon Sep 17 00:00:00 2001 From: Maciej Dobosz Date: Tue, 19 Dec 2023 19:57:12 +0100 Subject: [PATCH 59/98] Set location exactly once --- src/components/MapView/MapView.tsx | 32 +++++++++++----------- src/components/MapView/MapView.website.tsx | 32 +++++++++++----------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/components/MapView/MapView.tsx b/src/components/MapView/MapView.tsx index ac1af8d4d727..f7a15ba10d2c 100644 --- a/src/components/MapView/MapView.tsx +++ b/src/components/MapView/MapView.tsx @@ -31,15 +31,6 @@ const MapView = forwardRef( const [userInteractedWithMap, setUserInteractedWithMap] = useState(false); const hasAskedForLocationPermission = useRef(false); - const getUserLocationFromCache = useCallback(() => { - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - if (cachedUserLocation || !initialState) { - return; - } - - setCurrentPosition({longitude: initialState.location[0], latitude: initialState.location[1]}); - }, [cachedUserLocation, initialState]); - useFocusEffect( useCallback(() => { if (isOffline) { @@ -47,17 +38,26 @@ const MapView = forwardRef( } if (hasAskedForLocationPermission.current) { - getUserLocationFromCache(); return; } hasAskedForLocationPermission.current = true; - getCurrentPosition((params) => { - const currentCoords = {longitude: params.coords.longitude, latitude: params.coords.latitude}; - setCurrentPosition(currentCoords); - setUserLocation(currentCoords); - }, getUserLocationFromCache); - }, [getUserLocationFromCache, isOffline]), + getCurrentPosition( + (params) => { + const currentCoords = {longitude: params.coords.longitude, latitude: params.coords.latitude}; + setCurrentPosition(currentCoords); + setUserLocation(currentCoords); + }, + () => { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + if (cachedUserLocation || !initialState) { + return; + } + + setCurrentPosition({longitude: initialState.location[0], latitude: initialState.location[1]}); + }, + ); + }, [cachedUserLocation, initialState, isOffline]), ); // Determines if map can be panned to user's detected diff --git a/src/components/MapView/MapView.website.tsx b/src/components/MapView/MapView.website.tsx index 81ad13696778..9ade3548f959 100644 --- a/src/components/MapView/MapView.website.tsx +++ b/src/components/MapView/MapView.website.tsx @@ -54,15 +54,6 @@ const MapView = forwardRef( const setRef = useCallback((newRef: MapRef | null) => setMapRef(newRef), []); const hasAskedForLocationPermission = useRef(false); - const getUserLocationFromCache = useCallback(() => { - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - if (cachedUserLocation || !initialState) { - return; - } - - setCurrentPosition({longitude: initialState.location[0], latitude: initialState.location[1]}); - }, [cachedUserLocation, initialState]); - useFocusEffect( useCallback(() => { if (isOffline) { @@ -70,17 +61,26 @@ const MapView = forwardRef( } if (hasAskedForLocationPermission.current) { - getUserLocationFromCache(); return; } hasAskedForLocationPermission.current = true; - getCurrentPosition((params) => { - const currentCoords = {longitude: params.coords.longitude, latitude: params.coords.latitude}; - setCurrentPosition(currentCoords); - setUserLocation(currentCoords); - }, getUserLocationFromCache); - }, [getUserLocationFromCache, isOffline]), + getCurrentPosition( + (params) => { + const currentCoords = {longitude: params.coords.longitude, latitude: params.coords.latitude}; + setCurrentPosition(currentCoords); + setUserLocation(currentCoords); + }, + () => { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + if (cachedUserLocation || !initialState) { + return; + } + + setCurrentPosition({longitude: initialState.location[0], latitude: initialState.location[1]}); + }, + ); + }, [cachedUserLocation, initialState, isOffline]), ); // Determines if map can be panned to user's detected From a41803e3464b1fcd1897289ae9242b439ff3507d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Ch=C3=A1vez?= Date: Tue, 19 Dec 2023 17:43:15 -0600 Subject: [PATCH 60/98] Refactor timing logic in Timing.ts --- src/libs/actions/Timing.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Timing.ts b/src/libs/actions/Timing.ts index 0cef2fe4d2b8..9e40f088f1c2 100644 --- a/src/libs/actions/Timing.ts +++ b/src/libs/actions/Timing.ts @@ -39,8 +39,8 @@ function end(eventName: string, secondaryName = '', maxExecutionTime = 0) { return; } + const {startTime, shouldUseFirebase} = timestampData[eventName]; Environment.getEnvironment().then((envName) => { - const {startTime, shouldUseFirebase} = timestampData[eventName]; const eventTime = Date.now() - startTime; if (shouldUseFirebase) { From fdc5cfb0bc1a8de213b6b760a0f4b11e1cb16ddf Mon Sep 17 00:00:00 2001 From: Nikki Wines Date: Tue, 19 Dec 2023 18:47:12 -0500 Subject: [PATCH 61/98] Update OFFLINE_UX.md to include finallyData --- contributingGuides/OFFLINE_UX.md | 1 + 1 file changed, 1 insertion(+) diff --git a/contributingGuides/OFFLINE_UX.md b/contributingGuides/OFFLINE_UX.md index cca5c6286f73..cd45bebdce4b 100644 --- a/contributingGuides/OFFLINE_UX.md +++ b/contributingGuides/OFFLINE_UX.md @@ -82,6 +82,7 @@ When the user is offline: - `optimisticData` - always include this object when using the Pattern B - `successData` - include this if the action is `update` or `delete`. You do not have to include this if the action is `add` (same data was already passed using the `optimisticData` object) - `failureData` - always include this object. In case of `add` action, you will want to add some generic error which covers some unexpected failures which were not handled in the backend + - In the event that `successData` and `failureData` are the same, you can use a single object `finallyData` in place of both. **Handling errors:** - The [OfflineWithFeedback component](https://github.com/Expensify/App/blob/main/src/components/OfflineWithFeedback.js) already handles showing errors too, as long as you pass the error field in the [errors prop](https://github.com/Expensify/App/blob/128ea378f2e1418140325c02f0b894ee60a8e53f/src/components/OfflineWithFeedback.js#L29-L31) From d88c17e767fbf7569dae84cfec71680bc4957e03 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 20 Dec 2023 11:50:26 +0700 Subject: [PATCH 62/98] remove validateCodeSend error when verify magic code --- src/libs/actions/User.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libs/actions/User.js b/src/libs/actions/User.js index b1e46ec40861..67828c766147 100644 --- a/src/libs/actions/User.js +++ b/src/libs/actions/User.js @@ -371,6 +371,7 @@ function validateSecondaryLogin(contactMethod, validateCode) { [contactMethod]: { errorFields: { validateLogin: null, + validateCodeSent: null, }, pendingFields: { validateLogin: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, @@ -416,6 +417,7 @@ function validateSecondaryLogin(contactMethod, validateCode) { [contactMethod]: { errorFields: { validateLogin: ErrorUtils.getMicroSecondOnyxError('contacts.genericFailureMessages.validateSecondaryLogin'), + validateCodeSent: null, }, pendingFields: { validateLogin: null, From 253ccddc5ec18d0e764c8e3d8502ab9f9dfb1221 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Wed, 20 Dec 2023 09:23:08 +0100 Subject: [PATCH 63/98] fix: revert changes applied after another commit --- src/pages/SearchPage.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index 039ef7c2a079..d779ab856fb5 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -1,7 +1,8 @@ import PropTypes from 'prop-types'; -import React, {useCallback, useEffect, useRef, useState} from 'react'; +import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; +import _ from 'underscore'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import OptionsSelector from '@components/OptionsSelector'; import ScreenWrapper from '@components/ScreenWrapper'; @@ -77,18 +78,24 @@ function SearchPage({betas, personalDetails, reports, isSearchingForReports}) { }); }, [reports, personalDetails, searchValue, betas]); + const debouncedUpdateOptions = useMemo(() => _.debounce(updateOptions, 75), [updateOptions]); + useEffect(() => { Timing.start(CONST.TIMING.SEARCH_RENDER); Performance.markStart(CONST.TIMING.SEARCH_RENDER); }, []); + useEffect(() => { + updateOptions(); + }, [reports, personalDetails, betas, updateOptions]); + useEffect(() => { if (!isMounted.current) { isMounted.current = true; return; } - updateOptions(); + debouncedUpdateOptions(); // Ignoring the rule intentionally, we want to run the code only when search Value changes to prevent additional runs. // eslint-disable-next-line react-hooks/exhaustive-deps }, [searchValue]); @@ -177,6 +184,7 @@ function SearchPage({betas, personalDetails, reports, isSearchingForReports}) { Date: Wed, 20 Dec 2023 09:24:53 +0000 Subject: [PATCH 64/98] replace roundIcon with adaptive icon --- android/app/src/main/AndroidManifest.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 74e91caa91d5..84364f2ef7ff 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -18,7 +18,6 @@ android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:resizeableActivity="false" - android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="false" android:theme="@style/AppTheme" tools:replace="android:supportsRtl"> From 266835151f30ad2c4f3477f1b63a099efbfdd6d2 Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Wed, 20 Dec 2023 11:11:10 +0100 Subject: [PATCH 65/98] remove nullish coalescing from the loading logic --- src/pages/home/report/withReportAndReportActionOrNotFound.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/home/report/withReportAndReportActionOrNotFound.tsx b/src/pages/home/report/withReportAndReportActionOrNotFound.tsx index 9ad98a84bd1a..709f1af1c28d 100644 --- a/src/pages/home/report/withReportAndReportActionOrNotFound.tsx +++ b/src/pages/home/report/withReportAndReportActionOrNotFound.tsx @@ -70,7 +70,8 @@ export default function (WrappedComponent: const isLoadingReportAction = isEmptyObject(props.reportActions) || (props.reportMetadata?.isLoadingInitialReportActions && isEmptyObject(getReportAction())); const shouldHideReport = !isLoadingReport && (!props.report?.reportID || !ReportUtils.canAccessReport(props.report, props.policies, props.betas)); - if ((isLoadingReport ?? isLoadingReportAction) && !shouldHideReport) { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + if ((isLoadingReport || isLoadingReportAction) && !shouldHideReport) { return ; } From d5b1cbed1dff65f8cad941e7f232fc9618f24af7 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Wed, 20 Dec 2023 10:33:10 +0000 Subject: [PATCH 66/98] reduce Android themed icon padding to match existing --- .../res/drawable/ic_launcher_monochrome.png | Bin 1309 -> 2193 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/android/app/src/main/res/drawable/ic_launcher_monochrome.png b/android/app/src/main/res/drawable/ic_launcher_monochrome.png index 4eea447cd3cbd8e4724b325e93ee39c4dbdfa147..b1a286b6f8dd2be8cc1a884e9768b3322554812c 100644 GIT binary patch literal 2193 zcmbtV`9Bj37@yT-me=t*YMD9a$W7>FxpJ;!gs|~$BN3CcxmPO3t3ni$jEZtBGexo- zLs5v3`<8h3GIF=(c<22W-skgq&d=xjJU={FiX+-iL>MXz002Z#_SQ~(J+#k4f_#}G zuf^djaELu78~~73-e&=n6XFLyC=l*scN#$Nl4bLqgMOChEdhYabjY?B2mlaLL0Mb6 zLO2U2sK%|S_IArf0v_b8&Tzh&C!FU3cmdRq0?_5ztm$K! zN*|O`M5kuD&Em{YAbW>2fbyGOGQ30g`9=O8<+MgNL&s#DOp4%Wb3ApIlWezlW9{?g zHuwBLqU;WZ@q+7(GSYVOS}nJ}e^#hBD|(r1q0ZTTc)j0hN$?A8JcA;iN%I2LSM`{pH^tt!3J3**7(E)c7e*Dc#D=Z|GwD#&su7%y8iYm{+xcd8Ze*7&jPV)Wz` zZ?eRbA?~B50`G-0=@uiC%Lc)YrmE~CfmAy31!C$N>Ci~7j;wnvGBsGZ!MPsFJ)i|X;uRP&JXt<1w}3^p(SK6XNUDScz40HeM4Lcu z!x*L!Po9wAAtFk5U3q*4(?)nL`825&*m_XLEsWKa8)T zOwC}V%`k1TMno9G>p{}`5eZK-ltXu9os`k@9!9LTuh!4HuZka=RS+UXXz$bMzQ4aO z_cVkvxhDzseIcq*)y!>jP|%}}0apW6SVfT>&+1nVol=FHZwsOj1hQe=MdUa{AzJ1< zR!k*py7rY$7B*`w^yXD>8z>ioD-6T>yG&PTu~y?bz5bGps*|4@oiXJGZ>I*25au3) zzoUwsQQAv!P^V?tO6@`qbjE<|)7`El=$dFn_Im6?^;n%QMo~hWy)HUp8n4-RG^di2JAQc63wst%E0xgfm`ZW|06b0SGX4w~)Fp_UO8x|%qbjIr5mk$u?bx%6nRXCpW z*9qfHB?l3htn)dA5u!0(HX6qd3d|X!>Ib?5W-?zJ|FZgp>tMs8GjF8~S+B2KZ#0jc z+Nwh}1sbycp31>jh$ymjDH9pVp$uU$!iFLX@KcfM|OvGmzx_x`odf_{R?TbZ?Lx?X?9+I-Hl2y z`EAbl0w(8d#4MOA_N?R^A%FfyDBK9%ZDj*y+=(LUF15U%%n}u2^1blX5`oRTC-6$= zw2*5TZeM&K3!jTlGNrHK?c|Vd^}svZKr>>PXF#qG>~RV)@7aUj{EPiJRvUMG?ji6Z zbG3f0?LJ)GwVJ-F)`obUW@PSi9&eh{+SYyzyQ@4_Zcz5aPKeP}DM|(2ZSefCCfM;s zXBy|IG@0ixp^w#r{QX;xJ#ml?_)xX(a27)BUgF z+>c8#LV=d*4auPybZG&`Zd9KilKZy{AFDv4h0z$(v9Pd>>`qGqThO9BZ z=+~=GF24F{RYpDbSs!ljT@RD;VV#)|D~Po1+e0w%LPEH#w>U3>|2kpC+zFay`nKwocAS9Wz=?~2MUT5r(s@(wwdLc$JNUy&QQ z^9f;UZci>g;uZsuM@r90ct=ot=Pm=_xvfcQ&T0tq4 zZb9(x2tn(n@0ylJDpLDNiERsH<{L1YMj9&|HqGYTEk6F!GXNwRfm>VOX!l8c-BZ^T zvBV7|?d;x=`aM!lT%!mNQF%wHqz4nrvKDt6jL9#^5v5N|im6krp6{o-KqpJ1GswX})tx6g^oW f@&CRtA1(BcPOaG3)jF$~&iz~{8?-h3G~w32M)m5l delta 1296 zcmV+r1@HQi5uFM)iBL{Q4GJ0x0000DNk~Le0001N0001N2nGNE0FP!@%>V!Z32;bR za{vGf6951U69E94oEVWdAAbb0Nkl5h0mf_4xIgV_+;3aCsE#|Q_3L!~jqsea`ge8%{@!cQ&*Mbc@v%EC?C8);onSnND%pLKx5J8g z#2u@`b2hN;1h_J;5r3C7jStK0oFi`Mgyw6?9pvzE;#QZiOT;xj4#(rr-c(*;WnyYp zN?qv;RpR!jn&nmTc)0s~3QB@Q_c_rX99c+8SF%F4-eJBxs^;k9)pW5`SJou3a1SvqSvw(a_kT z9}m6lJ30rsqwxv-rI@g;_Z5cIOh!chhK+N`O~!L9$19<{nJJ$QsdjBKOsX|3xWdG> zOj`zdr#K9ga>7DVq*({`T4vECo(tvIi0>87N$3r}6&`zr#5w5Kt{~+a7M#LnyYJCH z5A#BK4*S~m5r0)J9%{^aQ?oGDw>;J?OGuJb6EchwQ^U46t_$ebTys{EF8=>4miDtqc3FQUW!#rV{ zqy?cG2i%xW$B@wOs7M`;J(OtHFex#!n-?0P@0?n1)_=#a0_)S3Fb5@8(<@|z37Z)v zY-ThQR@$>DAH#w*CCm%u1(aweEEmcjVLhG*cIh1ECEXEQ)2<74Oqa_YiImk_M(#>I` zYpwYP!=&6KZecvjJcSK(+w@cu<~m+)gBj_nWq(68Cu!c27>B1*?UNQbW@)WQO3$WRTb`7*v> zU4P5YI~{&2n%RkGB!~Ma>!E|OrsWQzGsvS4TTGj{hrUDqJ*acsajL{Q#N9$<2YT?k zfDd Date: Wed, 20 Dec 2023 10:42:27 +0000 Subject: [PATCH 67/98] clean up unused launcher icons --- .../res/mipmap-anydpi-v26/ic_launcher_round.xml | 5 ----- .../res/mipmap-hdpi/ic_launcher_foreground.png | Bin 4108 -> 0 bytes .../adhoc/res/mipmap-hdpi/ic_launcher_round.png | Bin 4471 -> 0 bytes .../res/mipmap-mdpi/ic_launcher_foreground.png | Bin 2707 -> 0 bytes .../adhoc/res/mipmap-mdpi/ic_launcher_round.png | Bin 2847 -> 0 bytes .../res/mipmap-xhdpi/ic_launcher_foreground.png | Bin 5477 -> 0 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 6370 -> 0 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 8437 -> 0 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 10060 -> 0 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 11518 -> 0 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 14383 -> 0 bytes .../adhoc/res/values/ic_launcher_background.xml | 4 ---- .../res/mipmap-anydpi-v26/ic_launcher_round.xml | 5 ----- .../res/mipmap-hdpi/ic_launcher_round.png | Bin 4470 -> 0 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 2851 -> 0 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 6331 -> 0 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 9945 -> 0 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 14143 -> 0 bytes .../res/values/ic_launcher_background.xml | 4 ---- .../res/mipmap-anydpi-v26/ic_launcher_round.xml | 5 ----- .../main/res/mipmap-hdpi/ic_launcher_round.png | Bin 4142 -> 0 bytes .../main/res/mipmap-mdpi/ic_launcher_round.png | Bin 2716 -> 0 bytes .../main/res/mipmap-xhdpi/ic_launcher_round.png | Bin 6010 -> 0 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 9387 -> 0 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 13361 -> 0 bytes 25 files changed, 23 deletions(-) delete mode 100644 android/app/src/adhoc/res/mipmap-anydpi-v26/ic_launcher_round.xml delete mode 100644 android/app/src/adhoc/res/mipmap-hdpi/ic_launcher_foreground.png delete mode 100644 android/app/src/adhoc/res/mipmap-hdpi/ic_launcher_round.png delete mode 100644 android/app/src/adhoc/res/mipmap-mdpi/ic_launcher_foreground.png delete mode 100644 android/app/src/adhoc/res/mipmap-mdpi/ic_launcher_round.png delete mode 100644 android/app/src/adhoc/res/mipmap-xhdpi/ic_launcher_foreground.png delete mode 100644 android/app/src/adhoc/res/mipmap-xhdpi/ic_launcher_round.png delete mode 100644 android/app/src/adhoc/res/mipmap-xxhdpi/ic_launcher_foreground.png delete mode 100644 android/app/src/adhoc/res/mipmap-xxhdpi/ic_launcher_round.png delete mode 100644 android/app/src/adhoc/res/mipmap-xxxhdpi/ic_launcher_foreground.png delete mode 100644 android/app/src/adhoc/res/mipmap-xxxhdpi/ic_launcher_round.png delete mode 100644 android/app/src/adhoc/res/values/ic_launcher_background.xml delete mode 100644 android/app/src/development/res/mipmap-anydpi-v26/ic_launcher_round.xml delete mode 100644 android/app/src/development/res/mipmap-hdpi/ic_launcher_round.png delete mode 100644 android/app/src/development/res/mipmap-mdpi/ic_launcher_round.png delete mode 100644 android/app/src/development/res/mipmap-xhdpi/ic_launcher_round.png delete mode 100644 android/app/src/development/res/mipmap-xxhdpi/ic_launcher_round.png delete mode 100644 android/app/src/development/res/mipmap-xxxhdpi/ic_launcher_round.png delete mode 100644 android/app/src/development/res/values/ic_launcher_background.xml delete mode 100644 android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml delete mode 100644 android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png delete mode 100644 android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png delete mode 100644 android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png delete mode 100644 android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png delete mode 100644 android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png diff --git a/android/app/src/adhoc/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/app/src/adhoc/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index 7353dbd1fd82..000000000000 --- a/android/app/src/adhoc/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/android/app/src/adhoc/res/mipmap-hdpi/ic_launcher_foreground.png b/android/app/src/adhoc/res/mipmap-hdpi/ic_launcher_foreground.png deleted file mode 100644 index f0cf22860e1602fa7a240eb84b43f69748ff8c42..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4108 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

+6Fh83Hq#j2BfKLQBbA)#ao zJPK0YPUoU){nQz1NA`Io{W%gK%`ON?Gc=SII{4c^- zHkcM?QwW-fHf+Bj?aty&9XylwEqRh!Vf0j{`Et^m3K`w+Pz}& z>|DH!G;U$l^Z{-vRyjQq`V~D&AmM$D8y1guxvcgz@)!5Hlh7ylHvATm#j4G*LcCpI z2s}hhPR!y_b=(zCw|RR=dW6m7Z`Vwh0}80m^C3bpKR!=E@L^}(3phM9TkPGG*xueh zQ2A)&!sr`h*L#+Q(`}Xo+QYhO)b6YzG(`OA9q>GsKYd5do2Uq`35&uw<5=pW>v zwiaHebzmCI<*VJtm9peE4`Mx#Jq{@Jw$~a*_t3lBDy;aE^5=84LTm4W8~a6Jt0Ka9 zI)(4rW{~rbXB%rlBjH0M?N@W$Du);G+;W)PsRP`5i84(~HB(B~u4D7uRbO>HPB!sZ zi{*cZ)f;Ay{5^RuWqNC(`5rz*KZ!vV2gx3(9HaM<+r}5lZgEP;Y)&-4#$T?0tgZ;& zRwqBnR}_oF3m1BH4}m^tbec^OdT4r!H&ZWM=fqNv#F)&7!vCno_QfeIZ+_2RLvIt;@JR1+@%-n-2`(|H zmC01`_K%jVI*eQ2fh({jiRI07rKaurGh_7j4pDJ406DJ1Yo%CYKt6!7KvZ;C?~1-Z zUU!LX1@IfgZ;u>)3)T9qQ!^7I%T#vb{}_8$#+GTG>@gIcjuBdVRZ8R$=Nc96Ty=fi z==b~`Q72VpD*x=+_vW8^W3tCH2Rkav)M>Clm?_nJBf=_VHyMfalFbT2@g~_JO3N lqNF7CHM-pYB6#^Yx;Y2^Z$gk|g8-QTfI|@))#|np{{hf3%t8PF diff --git a/android/app/src/adhoc/res/mipmap-hdpi/ic_launcher_round.png b/android/app/src/adhoc/res/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index 56e3594de9204dcdc1ef3296ac6d1ee53648a350..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4471 zcmV--5s2=IP)Qp81VJG|5pr=84iOL%At47zNXRuvL_}9ymDP1sSQcU3 zUCtFG3gM6&xd{mvz_6%@fatpTt?Pam-@aGfJ?ZN1nd!tF>el!D%rH~Odw;)rRqs_z zB&j|NNV19oW)JQA|C>U}u8dWr_X}M7)oC%d`Na`NmO#qUt6e#ha-DRlpqW&X-%={^ zv{r%d=`;Gyt_14yXHoR0ZY&?>{@PXgM1NMAKTA2`S+10N_9&(K<@kCZ8=N zj{2m&z2nfB<_GeVD%?L*n!M}y?B1xv5>)0Zfs~T`3Z*#jdybNPLWPDOOwd19>#FsC z5C~KIUP5DenELd7#>d)BW82A8e??!8 zsT6zeMp9M@#F(FYO@sQTF}(lhR<(*`vB+2F<54?>XX!ju1#XhFppX4 z`Hp0=leKP&KH#D38&RkYl@^nK%_4kNaWvQZSyiCRpia9&l2MSk){8b!gOhn3@kyQI zf{;*7{mT&~U}pv>?ZBp3~b&o-ajA5Ja(lFl``(j7-Q6>=8D##>zHStBXWun`)2|B%}9_VyMffDwN*)9 zs0xYLFZEC3C?CvH$2H5M%gU`CiQJJDWz^}%7tJIa6my^RT|jaWY(+fCqwfn$Ojt0; zV|s&}J`mWPA;9Ji1@^nK>MQ=AK9|Se&+A#*3Pc5gJhB%b*DQ;Lq@%gtxr@@e8g;9F z(f1H%3Yw6!)1HEFat`MJdwLj1eebm@K?rFB^57mI=SQo`?4HRj&F9KRpC8EP^*HIC zXpo2Y;Qd(oCWLha_U2>Oest(09m@V#+K_mYpkuX)))+fimvVm&c~2(Dvm!z2YGXCs z+Hl3#?{PKSk>THG_ybL$T{C$<^2ku$uX!vv@j+`lu@6;;y_xT!YHBX3T17KIR-B(2 zket0p&a`lgupHzgUK#6bd9DhuKlX=5c>mt9(0{Q7qpWo;@LVJ7WLwiRjk-E7da!pi zrxi}EZ+WM__BmuZM2`7s;xaBLKLo6ZRvDXiH396GZJ7ab0s5;^y+Ec_ZC{qPo*ASI zEjIhJzLi!_Yb)sNsA!dy<9RFmS`YdhIWNj;H9gv*lO4>zx3u}GK-XzU`dW#mnqz|W z&ES#jern+9JEr3M8}qW3J|4Tr1-G89A^onu*~GvDmNm zyg1sj2_%Q?*XIza6o=t*|D%EZSd=wECK-(n5alJg&Go!G79}q?a$qgGdF@QOS)qhE zHL_c|wx?sn-9sFFU>9J%=DCi&S?ccE*shwOeyBgo7zVJS6p$Y5B(_7jNq(WOzM}2R z{8IYOh!z4DLeZ_~gRCi9;>$`74gCBlaY@JbY~h%|OL-j=ggzA5{Ndupm1KS1W}j5* zso+t->i8rh7h+N8o~#_ndE4joLgy<-7&c}kESkJ>*nv~7Yk~+k4W&U{#kMgaR#%|w zx+(cx!QvSpRIoU35oVZf*(>Xh$#oMmh;MX z1ktL{vz;)I-_(K`fe3PF5!C^E^$~GA)PwX9>ghTcCafSXD*aI96%z%W*Dj&5agaq5 z#6fy@P?P930@*@s*-sN_X=M&YflM-w~4E893|`RU=p z0F;m6X-=~+zEGb8(bzHFMfoZ6H`dV{HQSz6F12sg*lRrAP0@nk&2cYt$y?bph@3J& z93VanltX@Y&SnH4h*lf+Lacva{AcOc2~bXXs+g}LeS|vtO13NGvJ@X~*f8n@{b4NS ze*WdHY#KzK)K?rJC(61K_(K4K_=YY3OSew~2QGD#1K@>6VPtW&w8zADX%Xm2Ix zl`-vb9_q<4%Q8;~dBy=)!4Rt;{t$>D2axltNf54ha$dC97wRL_(F51Lncqs85uL3B zy%gL27fTR=?n`85yJ$=U&w+Tc_C1nIL_WYwXKPkUGWoWPBq{d+1&R zLE0*o*7r8$XpV0uJ-&}P0PSY0k{Am_5M?GQjPV2ciE8GX?0nF~zG5Anpq*)#rLkRG zsRS99`DRjMb~wlPGN1OaI6zIWVoN3hduxh+&;fwCUnjs2LY@*P%1=2ZV;y~&-;#P( zY9cii2~tUFggfcoyt2(kQKr;^aR7>n9R`dX0SKzqeGrBf(!+*$jO0gsggW|0u`qEz za?!-Vi=>AdHz>Q(-||*A4MI061f@dTIS*6`^-0jC6w84ozae6*qwBmfZZ&ZsraaWZ ztO0u`(c?2%-IB_~3t6eevkPgm5Ll4fs=n@?QW1*(_evQRk*Nplg zXxB{3;U;fujv*;&k5iXDH#~E!qI@#ebf261cZ6{$~Kx>)75PA|O_#5TZxn=0=m`_3#<>UE6h ztm(oO`+I;?L>&{veNP{8)g`%YQ>|oCODC=(-8Bw;6m>OgsN@ce;f;JG@OgI8w6CCi zi@BZvbgJ@G!}tZKAB*eejDXhf&WD&~i+v(0c@9MOj^nsLG!~k4>jLg2<`i+B8ldh$ z7cs5RDAL{D{}@YT8~TmbtxD01t-KREE25<{p}%;-F}$aBZr3mx9P>cP`i1an+BVof zPsq6OFDZ5bT+cez+6qUioN`6EvzZK@oifDIAhloTH z&TUHyvxebDat8f*5o{iN#G<@Y5UH=t*ao-knFn6$p#>%>Yy#;lO%1Ve&UPid)%E!A z?aI1@bzDFn7pQ2S%XB04jYn&BCrk+5_**D^^q@uQs^VbGNywSE4jLVvb4~L-beej* zrnAGM`l7~CMDPR3-ps51&g$z5^Yh=cC|h0Z9adV=y>KDjA-XDsweQb$4d{E!<*{&L z^j5ZTbOGPm8-Je_2t}#DX2DIY=UVQ4AQ%wNQX*t%goMT`pXGW+er{=m2(SGv+!2oR=0=ptNx?<+XJ>=K-r{Rw=z<7+6(ItLg2_62;t`U;dka~>|O{}RqG zy#%Lbe~jdmT{{?k0@7n&+={xj)^zl-?+H{?v%5)Z-lz-PoLtTY_Ti6our*Vy7s+X} zhC5nHd(!t+O0*p(f}Z!1^WlttaDs@G3xB!HE#~6iufXT4zJQBwUx9DR{taiJ{|rte zS!7+8H+}`5ym66RT}k66P#uKz8VB1WtGdjeghSCMuj%9@-;q%0u1g0< z>h~nnH)2UQuwcd=*Y!rz0&{jbN2FBU~i){e?OfEycBZWz3o{TwRkDaom~j8rM?euChp`| zny~YSWz%wz3&aY-GxU%C?!?4HzT zva)>A4!+W<{z>yuI$1x)vUpqdPg78zy2a2~IHr3-XdKDFWl?3SVtiG5MYX2pjp(nj z1R**rqq{$i0TaSSE48>K7g z%1RKI;U{15|=XHXO#1m4}8EUG1fZftdOwY;gXd*XfZu6aWj zD2v9gQC7t7WA7xMW<@C%Fqr*P*^&M)4t&oxr(9qg5>Hbd^lfWg^M@|rZLtqYwHE60 zPCkYjaQ-ZaF&8Gak*?z#Om+Iv-M`j|4v3C!x4RpnCn@df*m9t9-(8Uede6ypyL(U_ zs;jl3wk>#H)F<^_hf8;D7lxCe0e{-TxQ}XmOLOVwMz>0o3s#9ECx4d)dQYF-#_LdB zYQx(~bn5VnA;n)&kGsQhr3sPTJYi00PXfKC&*(dqjCymp{{x9SO#MI#XqEr~002ov JPDHLkV1jPo#GU{E diff --git a/android/app/src/adhoc/res/mipmap-mdpi/ic_launcher_foreground.png b/android/app/src/adhoc/res/mipmap-mdpi/ic_launcher_foreground.png deleted file mode 100644 index 8070c42691fec85ecbc047e579680e431bf2e193..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2707 zcmai0cU05K7XA@K@3It;qJYSPK!l(ensksVB|vCVS`t7|nt(_^Q4vk7n1r$-YY;;y zCV-RxfenfZ1VbW4R30b<2&jN8{bm1rf4sA2?#y@Y-1+XgXU=!#PNA>2tE`l|6aWCS z?ru(gLQME)N{9(pd~8s^5WsQvUiJXcfS2AnBP#R{M7jBS0YH{M0AQ~Iz=m)OI|~45 z5CE7%0sxEv0IJEQt*5Pp0kN|lu1>)2KjSf@>OKHSK6iJr54bocD9ud{;3~iB-13z% z(4|W0$ZihgxTj8JEQU6p;vjBQgAbYpH;Uv2P0i;{l%9v2BPxJc?Xe%vnIe4+QqXoAjPi|g-7iQ6-bwv>*gkJ$q) zYc?kp!1n)QS4~Zn)o|{=P_yqVzdTULZ_*Yist_EYYdUqEwd|-oU;3iL1K+Ke4K;*> zX*8Qw_d@DnAbXFV)Bar;6{w9AyzWZ1GHwhY3y@>w8xsKBgSny>+z$#3lt8H0;IN5! zh3l8gho(0!B`=tNVOvAEd3;?6(Yi*Cvks9g(N7=pcFT&$47Z(@?~#k-eg#ktOMAisc7F zw)fY-^Z1?Fidjm8a)8tFeY3Z4YWbiOE15gUG%Q8jH!E9U!yyb2vM8J9Gs#{@rol!w z^y!*c9p&h0ps+Sjg3lg~CyFHQx-ILey>ehI9pcQKgIlunK+^-h^-?D(n@oDsD-?T0 zK_g9ZJ$k#6U0*=miK9L>Bq+$A_a`2I>WTnY9?U%dvv_lV0o9=wnDmK+)-t%YDE5}Kz zrr?E}+Z*)wc@U)WDS1|1@wri}24)@}L_LW$q}i=j>_Lv}Zp1L0jq#eIeKekA3Xc2z=#mlFU}dD&)#$hJ-m`N(Xn@7L^2Y@+0RDr z4xLUq2<-q$SQUk?`REu#ZjupUv7bMdb4U$9wOZ2Z$+wfKeFcZrC=@^0Q4?2sm+|?{ z!-TQBt#KwQZ(D4m5%+!}6R(i3i@n6|I!H7@z9R2m*ty>lSz^)s1o=GAk(PaY{%}jx z+MF3Ahjc9M!xe#~C{$A`CHhTEwn|D+_;4QtI4LmCVBTbI40SA{woJ=%$ z4))I`?Xf-CrEY~1^}w}Qr|iCK?ZA>9W`s;1V?=ocXAhoYli`I~6BshL%bjiAUJh({u z8IoMZk&4|zrii=Mhb{cEzCE+oFpLRhl(yKTBO$Y*9=xc%XR4zoyX{SbzY_`zjMONu zX6eO^8>>J}p_N~s=P$YI@S>kZ^?RG|h?YphL&dE5$+O}08~X^guQ9ldbC)?Ua}XlZ zu@iTOe7QNEch_7szp)qS4e5FO7SJi!0-qQs$Gv8oD6{yeJH+PUF`f#lm4Vf%c?a^3 z>Loh9GSQ~zpI}9G88RL=sUZ3dG?-gEd;G_jtk(3T=ZZzPo(bXAS>jU_ueLez!;#|Len)mQ8CdG9u-z28Iv5DpNrOY@+Jm^w@Y@5+~`Cq4#ezLB%8;6ZT4u|dA zhAyzuUD`($Z>X9xMy(>QNM;Wu-hJL{RqR=$+Wu;fCrsPn+q-3kl_3Y;jvAPM=*c z04Lgot;(VdMVFuLNT^odv{Cz^p5f9?sQyFo`05Ol*h<^q-=^A3P@b_Mwl1-}!qeW* z4JMiy<+2mzs$O{sh__?E7W94oota+1m`%~~g>luyUS)_8;Cdg98Of0273kaRmwj~q z!#b;I8jGo7d5%wLk_C%$N^Q0MsU7uwdW)fPh@{Y{vCEx2`p1n9bZ#;uWZA7M#3q-D zF6W#ZTZ)V$<#NikVv3yqOnvi4&Yc0-85OImG;z4oanqN7Cg)Z?uXc9qZ@hf}SZ4y{ z$Gnn$!{Z*k#qcWr|~bIt#tp`JkY+@WK1s2|@{d~b z;)BXkmwC;MK=NXZi+`^71u7yfvj|NX#lTC)qvr+-4<}|_$cY#Fb8&xp{b?AhUW>9w zi?bjJwR6Vwm-}Bu6S|<7`5DogJ|yCfbtvJyb*;}g>9T}!+wF&!V{SACHiQeM^-y>= zQ8BJ=;&$CFSFB-$-~@XG(?PAS{%QxU^9lc?Jp{uTO+7k7_5Q8UpykVN_`La)$@lAh zJ=Syu1S>@$URNUvO{XknB7ffhAQS`b?(}dm#aFw4G4da`z@E(a{MYf%Yt(R$b1wcI zKWzxU?k&n0bw5b;VDg^!eNH{50aM0>%XvK&HtE2ich-Lkem@@=q`VDJjM=*97MKwG zr&*TQG3g)|(?ZX1i_CR8D&*3sFx#QyNX@&ogZ2-63>?<4>WuulF+7lrXQj(xu9*lc z<(Bb|V*2Q_2S*doX-{4ctF6Ou2PE?U#biJ`;%%v?B)1%svW5S+L#fWesnKUrV_;Di zVuS#gLCnmJk3x*iECP<6fSFmqAQnat2n+&|5RoSS4z+e=`2|)?UBt}FY5gt}FX|2ODRdK2@ z8WYFX*_deJKqP{S!eh`VPHEIcSEklK$x_+f`ouBtQOHUo-cmvH!OQ`7%3}@9=)jTXlX5c0RWybLTzBLC^Hgu8|Cq z_1Kyn@e4U^mFm0%r7~{~ejZmUa%+@|yxaKqcO3Lg@8~b1Le~!(bqhdWNC(=4%zL> zu;b9UW*Vqe=C0CbUCf%{0wqpPP)ZF0)~_S5&aHf-YXGo8oq1n*!z55nWt&%|mdn=u zF3(wG$E9J-lnu~jHgoct|7r?wCgp;0z=m}L)}xJ;Y<&oB%g4$q$AePr^mHvv&$*l} zNI}FxgN-l+4=(L$JM!{@G?3z7K;o?pLf!DWPY_74eL$M`GRSLUL4GSvV0A1=t_c1e z_2GTBaXs6Bl<*?RAEetGaXNb~(~M|PV5>P^cIVEpkT1*O{>?($83ekw25C$P$g3uR za?&)DzCbItVjM{0LeY=_U5+&QMbCbuvnr2b4dmjiOfxs%P0hi6<*r^H0jrT;jxO_j zpitTcl@IR9XPO75H_`fVGK-IuqIzKLbO32?l(i3^ygbr<-*F`Kc{4|AGyA^IOAYWx~j#dHVP&t&IXy)vh zHp_Aj35!*~!%d0}?WU&501>d>N(skj8d<4L^}zpd4x1 zK;)968BxpA`M%^dZUE=5u7GmNxlXr~K2ZT1*jbPX4fH+xJ`AAUm)APJ;I;{34H(R< zx4Kyl?P}=OxE)U4$Y^&ql@}5&%?XcAAEq0#KYB1wYuYB~X zc&~9go^cX6n^Nh@m=@?VHRew8)5OS$k5O1Gi%v#nEivkbW z81Tqh2zBbRkPCmW1@R{{udtDwe`^@PSTnS$Nbf|SM>F)JBBeBM1s?b*48Ssi%R&ys zg}>LheUx5;oSO^kq9}4;Zr$L~iK*J#RA_VYR-(hsfJfQLX!6vJj5z3LW{umP zLQ@$)vyZ1St(@lP2AX{{ zrvqt)$n{s8vCdtM1NoiF(|IRtx$s%sLa1lquudS;?V7h@Je)8xv=L~vdp`8vz8VH^ z`GV+j;e;)Z|tpYUN%?E{<6_?iKF#UPtCBXrE+veDOYN~tMmrtW`bQz_&~3{ z%i-AQatqPAP!d^nFX-?RkoTvaLvDZI41HcR3d@=)*#k%5`QS&sZF}EBBjkOlAdLtH zHn@vOuVjebErS>0R&9n-Bz1n;Rk*t723%cy1FkK-3IAUEC0t)|3qEsRhwIC4!PSK~ z;riciLB*uY_sb$LLfHB@e^k=@#Sk~P*4M?B)`SsH$*U*)g=YW?9!gY~%&_eB~|pYS-6r^SwLpdBH8XbMPCuz5g4infC=;S#SgH zeDEz?p7+Ir3#r#?i-(m%&y4hi#L3=uyc?ya7)DFGr!89D-E*|bp3%bp=T!o z85G+Fu*&FVJ!7Oh{ z#9cK6SV*A9iugi{uAN|E*jwmE=Pkaj;Y`E@?lz~wD`8{btp|`pE6&BI-}`D}Kr=*u z@=p8~{;LWZ!2>(j_<@7A_Fc&!%^oH;J@p#K7WiGumq>$q0YPn`0JY#rgv$O>yCE2)SB`Q|z$xGt)n(@=}BYpXnX{t|S{; zoc=Sk`FJse>{$UJy9#PUcdxh?x@RSR7C`X3i+^aBHDwX$gJhA+{V4mDkXX{ibvD{Lac?p=QF6)_+oWDLGS20>LY&9{9f6aTu3rV7MIzeJpZVTW=1G^67f4k zJ2oUJ!m(iZ9A#7dHf3LG3DqoEete1P_G=D&mXBoAC0H~-Y|;49*TiRvwV zE^#V$F4B+vCC+xs$b+SxhKpDVfoW_IT)S6W?uvj@~)8iQwK&IwuOodQF!SJcR zepLx7cRq*Kt_<02Rhk5E_?>hZ5s&OCLH49Wj;tYSqHuk9B07Cp9XREJPgu=to|%`N zT=YHw+H*1v+2CQXGl&Za<+s))zt=%46tNh5>~1#eZB6Oe`#ikq5@D*q8h~-MV!%)H zMF1{|2dT?~kU$D3QOe*HWE#RllA$z^Nr4I`TzX_$7jW0~6a){8g8YCbHfHcV#WW@% zO|$@OZXPgCQkj0TC>aMGovX9z4fwgF>1iVCVpV9)Q_C*}5Va3^>lj@FZm+G!UEcsV z7|gpsaUhAF2%CzO!Yy4 zVwnV+c^nT+7G9grgyQK%Khd2CM5>3ioWK>f2Q$2nsSHA{!Q&wsapA&_@W)c^W+HT>Oz2$^tMMzIZJHy?v^!c`;zK$W+&J9eId? zNdIFt_dD2XnZ7FF^ow!apcn>&rMir_NgX#`_fe<#hw@J|r`^@H%Pz#&N zJ*Sv^&ei+Z=~&+%EU$v$1MTq;ce#dd68p_64@HY0;Y(W!;%}vy7*bL(RPd#4L}zlU zr?^vu65+Pg#*4bw#VXpL=a9{_?8Ov9oBq|%JQQa?eQ+iULC4-pM`NSQjWiSSpn>nm z02cmK{a<33I+YUfVHd~9z#H&F+>pf7OfT+J*}Ze#*!Wb;G7VS*AP{+n{ru|DW~!PY zMaubVs!`W^a2eYs0LB?O9lav1$JCU{RR3i8)gK!2SO7yvTP{cNqFD^&Qj|k@S60E& zoCHz9M%cDIvd7zjh}+y|_2i=nJbZOj_v#8@v02>r5mobY!3ds6D1E{yvKXh|wdjc$ zANlqE=JSzeTMcebPG1smwo2tQ0j9P#;=4n6KrYiuqcC2Ip7WMtzFH!yFL%p+>T(ll z*AWHjf3v?=dWFoX>|mqyn(bt2 z-@mIYeR{pFdBu=m8O>KxQwwmSLGzq7blKZad?c)O?VnUYnkyW z9kXI)k1u80iO<&3S+OG<00er-IsYL--bhl<*H<|tSbUeM(-wBUec-?J6*s411`4d1 zADnyN6z)oIlN$ryvuh~>FI8c58^)wOF1z`30|*n%`;r@ zTr~L`dX-*1l6`~r!OiW7^dn;{!N!{&x$4AFYO<090Z;jr!XWKYv?hxDq&8xwW^SA~>Mgk>|6{MHj(5u5oN92C-6He8{1-OUsgt8F}_m)`cfmkUY?6Vv4 zunn5Gojy#>yHX1|YK-2I`rye&CTw3GXV(%AO5~Xrzo#7Oi=)K}0H=PDxX;Rqj;aL5 zWa8>?UuG(H$WdX2u8Gb8xi{;_;jM?V_*7P|=Bvz`V z_R*=UA_k}7=fU$ynN;5zs=>#bZj-8jyVj9!aK267#+AKuGmOvze2m3i7uzAY+XND! zW!!K#d910e8o9T7Zln$AXFjJd=zT-gv&#-XA6Ro;)&=ZrtL3WuGsx4q>43f@g_+qe z>B}utJw7B~21`V@4>`A*5FAeng|IPaXmOsd_;@5SRqxhIVR#rVu4i;4$JUy&|1bF( z^TG%}diW#x5=ZK?S&=FGN`^eyk3XgC{$2JlsB0X2j=IW`JabXZsTi*!^p|9Tb6zqr z{ATBCGK2Q{@Kw2@2y(4{)+w&bCG#)e5!=?E4*+|tPR zG0P%prx4g^PRuLqHu9x~V>$A<@rsqDZh2OH;@sJK2ZArDNJ**GGBSZLl!bDWlLxF8 z`9Yw|3G6XEdH&%zoRf3x^Up5t&_YxLfVq}t11&y80DTXOINLw!A z>N0q?F0!dzW6sJTa6PytJ7n!A^c>1SM@`w0W|L_C6O#)Qf!yBhlfTU%Fn(a8xT_~p zd-F=JA2qMj36u0REOCLe%OJ~!9$23E%<*o=`erJ&53_J2mxtx#|J2PM#NV|al)?hI z+NpD-uz+_0A)~rkLiAXVis-YFLzVgp#6WO@#E6l>c(Ve5-7c{hQZ$w^Cn9sWN4!EhSK3k~ z>gi9m2u%Ek91;3*O%aw-J6wa?vs<+V^w}t3%kPL7${$-v`U(9@3 zjl#h-J28gJGHNFl-e#2BJ6C%dGEvJGS*;`d;E3*MGb{DP64{Rq>SWPr7%7sH+XXGe zo%ODPAE#r~AVamV8Wns;tT^2M>qV5eJ@l{AuEcX<1Gz>a0s<{XO56+rY%bH7+iWt*f%j|hSuc5+sI$Ro*udtqQK>G(nboDUhAEF1 z9gQT3rQ{LKQOF!M>DBC18%rt*-E|`ZGp2}pQLLVoUu$c~TqVmBtPgaU1o2MZto!9j zeIv-DqOPS>>6>F=7qaJo@ftGVAvV!YhIjIEODC=QcBt)Zpcy*f%|BwiO9|pnf^P>? zlX>yM&#zEK6+Mm&eLDG59$*Sb`&*TnPsSViu?&+9nvgO)_|i0je8ss=Jy|_6)aT+s z${yH^FTeybt9&KjDYu_8EwguX_rC=jEayv$>^~lV2JC2vZfC`&rl(yz6{t6p!|Z8f zYq>S-mNy&iwOVHsY6ch1$6{!kP%SnQh2C9cXPpZnMyS){^yB0mr+=rOXxvy|GFqd2 zB&mJpv#8{Q5(+Yvehfd{%QCX=tSG%zSAXQ@LEgnyqp?s}{rc6m5YOgspJ--uReJpV zVrlb}Yw+uZjdrr-3n*bUVOe7rqh+)n!zw%y-sv$?ktA%zwTF`2>=gUTI(WzT0rXrY zk~~i;)p}X8EhND6>yDzWteK&!Y%l=-xcOy$*yFLQOOM~CI?%=GU(a&xk4E*p*1x*f z%govr)$8YDJL%`Q7iPzv`l5}S!c(B`S3qFId*m-HDXW!A?--kzsgBAB$k?WwA8O^5 zfAPUmLC#=Nj#0>Ce?QbKbo73sxFIW+i>g68D=!%0m{gsLDT8@Ip+r3Z^DjbXXzlEK z-Prd=@izC^XzH0^Ao5TL#Vj8OiHgu8vHguqBt+xa_rp>xWL#wnY-bW!NJ1U@*nH#v z>N6DY5wY_l46v|DaubHe%fGgwyP+d;c!!1rq%R%#V8DiAn8$YJs3x2zr=;7jp%VG7 zN9nhcKLhU30E;!(V0*3rkru!h`6nSf8QA--Rr1q5J%y~gH*n-(LFh0$Zfk!eAjgZ$TGoQiHCDKSDtf1Y zm7Zm>_70oRn0eK7ZmoWtn?|<9E%eaf%xJHdL_cW*<+ z@?poejr`<(4SOj074k4j!-qwG>$Sf|A#3w9nq!dCmDLx`&lrovTz}YI)2cZ(&hs&! zx-nPNtfoO@-|FLKL6{wVFLnOj$Qbo$>{7bEAI^KLisQJdix+=f5S61J3?KNOlv>K1 zcfV=f{*kx4Z_@U;(6UTQ-l&2tyYb)KKwp_Kl=|g@XrULs^S}$`dBzRc^4vW7_5MV+ z=X$mHUm8@8(b_xz0hn@xc4ZY$k~OJBgtf@E6KL*@h<`uQ?Ji5NAejJR-l0+*{vv7q zS*LlnPQ{-O=}SQxAMAS?hhUHt z@=O;O6D_?|o0Oo-SYFX5^cyX+-M4ILYz_X!TJrT)Ox7L;z32f4Z=Z*{IOH~1(-X(_ z%Vt3pur5h$PRj5!a=!?y!21s5xinkb7|S=C63TJEbp~gnKNT7_g-2R~$Hu*JyN&2s za+k0s9kkrDGAV_|XZ{i!evBK|LqRi&d2^~Q0;N)?h$O`21cjM*Z1XQhR?tDzelwJo zo`#vpOyJ*1o7ZPXD5&nQn{kR{2oMXv8{Q^*fYz+ae?8+?qL4k6p3_OJ?M&dsx4*Xa z2#e8Tm!i4Sym`bp`}H>8sm#nEW4E}^xrSwzxG*U?>diSfHM?D>+NE@Sm$9b+O^+OZ?{D zih85>)TgG(^y7D+Gx@5Tzr4~jjc)+Oo47UY1!7p(QCOFyms&_?aPeqAF@=`>66#pw z;SYIT2RrTT5dMA7$PC@%WqBqk)>i; z&-Lt1<{dn}3$;=Q@)$S9=W#j(FL7nU0s7T-{KqO@c#`Mcp<;mP6vy=JZR*1M%Pv1ly_#WR637sF)qo%JRXK4jZP}nMpx&Q;{5kc*rv;zO3WM9i zRxlspT$-C@T{Q;pQ;4#4fjYQ_2~>Caz2L+HSmG4&FcfHip8{0vys+__N>G?0S% z4(kk(uYz0@WE}E9Mbj$NN-lh{iQh@_zybL!A=d5rb78)TPHw-AAGc{}jIv1o+Sk&`je!!arF5l5Ev1w$ zmI}4RnuSoI6s5MJ+w*?U_qICU{ASL%%USN0NN@U?=lMz8JL~U1^IvA>oJi8`bUS%X zRFWl{K{!?oR6IhawR%wJ?PQTKTS3;rxDbzoAIvB#Fxyo zX_(pm0(H0xNc~*2i9Vn&=#yoCOKj5gtd@@0>#$ijkCA@ENjxX{zePs4H)sM8ZHo59 zsSn$FIvzytPNC$Bv6Q^^6eafFRDQZ0 zm3uX#a@WQjQm2OQ=-i0P-Pm_Mn^O77)|5QghmyCWDcKlLm0iiAPJKJx;BXMccu4M) z=hzU18VVY_pml`J3fAEd9OUN(FzYdUKOaNMq#jfr)QU>&>QkxF1BFBDy1XvbscaZc zm7FQYcD+Fx3&xe3_Mw!~QH=R77lLMN5l|gT-z3Oa10($)tN_xtBv3M=AC(_#W+bqP zfi_4+Bvm$#^G{tl0zNy0xLMi~SHX>gVo#d%b22I_hwXb+eeb(lnZpLZl@UL+HY5SxvCscOhqS%tw;4VJ%vpD(wsGPl% zD0#WR(A|1+iW{sCWO`p=*!BIi0>%m<97^3Qy+6F78z;9sX&!*k`Cs})e)befJ{Zmo zrPRca8Q(ONN*x=>Wn%2ClL)!N(g>^&T=rVri@Svi9H?}mZ8BH}?RM=@9N?Wd@+ zC&fSS#8>2{^SaRn+CrO#eyEVs9y6A=g60_=N@bo^Vx%iqMVB-Kvp208Yw(;;em*0S ztQbM19!(7cbO7E63!upCfs7nKj9_8G+9IzBVUa!Sv`{J)G#CI8ZhgVx`{0Wlk&v^*0!9|o!JPrpD@Ow zJBi3ypVupxJ;2J%obWN@O5DmQlFd6+l1grBlw)tli{2-z82RvY^rUO(!e;#&gQ;>b z)ljzB!Bht~+V2hH{f3Kd$m0uyJz!&E2K@8*VXoW-o-~7UaB>CKz)7Kc(mZb^!{GLg zFp{nTe)lJ%ZjGM*tD&kQvMcG7v}o|%ZX5=bL}8iaroA9kj~DXyPUM?rQezY18RWsO zDcPE6EEnux@e87M?jY#D(*x%Uh<7%Pc?H}BEIfz$_BrPsZBJvpq=7az9_fIvRq zf6_8>#!eSrkt)ZSb^uQ4&>+a+m(g!2zaSpZ3JHL|9U$QMHZH_OqAMU2q z)?)V(YNsU5d=`+&iCx`2rFQkXgJH<)hBUqklrP3C1c(0H0~Ky!A;hW30oo_BXTV^*p+lmnm4#E=W>xhv4N)hvs2Z>6%XVi z3S@q~)YK%xY!=FV>VZBRT*M80Kj#(#ZRp#zMCJspdJN-gidezRxDX-3!`d+VC2;Ax zEztHd<_|1S4yPaW>iIC^WV)-#46!_}vv2z{@^`;~o@`uXDb^F0=Q=YU5Wip?{VGFAvW0iaKqU>(8@ z^#JnJ-iG{6q=~oyXV@Rb4gsg$ZGkg-BTV{By>2;dXG_$~=VzV}`B#5qelG|{0q}mk zWZ~WQYRWm#=XZid$JkKZ7{8ivDsE(ZjE=P>@>zU!R>661#us}gX1X6+z}Obn=l6zS z6#&Mi6Cj1~}^t+#P>(Lg7T|Aljuuu4+e&hiro)2@`$ebv# z`)E?e6M|L%dFo^{sSn08RZwNFhp2+5v9@;HO*q&cO4* zUV4BBDLx6{k-AilZW}NVFzVa&35-N}a!*r_2wWKQYfw2Pn-g-R8j^T=$5aTa_am(z z9mWdWa4l>ATT=3{c7~2fEyH|>60PBj0HF5xk`5G}wh_F5d>BKe77rQw#01OhHYwxS zn)o{DC+U@bO*qG)sQBDy%VYO&X8zuZ0?S#`k(Acnc`4C76zCsk2PJ2a#D9gUVjku0$`BGb~f}0 zT!0gOUJmWt7*X+x*css1sDhmSsZH58@m+7xFL`Bvc_$VPHRSaNB|!jW?vsW-0oV!F z=XC&E)KGnTeSi)-#~@qjZ#E~OltyxE%oczCYwM60kuZ@QHT0el?C?11s8RUXsX zkk<*7%g;t{XB-Pd?6xf-{v%5r!^@NG)Lfp~lK^n5Ku=DOrrb8@^ZEmy1OOw91AKpg ztznb)0s`!x$wdQjV#upOHeU*_iG3m6=A63wDZLn3jk%DgT??6k-)z%&U_i@4 zAF%r(09>BJZD)T#mc|&`3K%$`VPn7%*0S8$JnO2a5&Lx;Nuon+jmT;6^O-FOEX`ih$b! z2wVU=lYW(^bZcoP0EMZb#_wzxqB1qn|_)AG83;z&$Ml9Er1a%YciktJ6_0NHx&Eg#o>nHmWBFY zhR?vmc_!@z1dGENz7M9)t3h)^w~RY0Mb;1HT)W*B5`h?zYTi|u9GvVu5F28g0obG> zRvS`2!WRMj<&p3gW6iIsFi!M&olpa@&*Sr@D&?wkuH9CEdJfERmy$JcOF&x@4?iR> zx0>Yl6(vFdxOn`q$(A(ZM4#6QmCLnp+Zo4*go%{F`F0V2SQ6!xuan;QCIEi7-h>=} z&)+1!uP6}$z#S-)et{ExUI(Mqam+9C1RvVH0T=-8oFJ$3hT-YsZejp=0EUkR_^k6Ok6t?n& zwk7ht4?{c-DULHa4Qu4Z_Ng3_ zwQ+fj+j}_2ZU)#jjq7;_`?r#^C1H`bTM>LYMi}jY)TSQ4&S%K)1AG!dA%QmZ?fL?^ z*tm3{uMZq}RJ-bNr1H*CoQ73WIkrf1`IqHc=BHikfTg48M$+t6jaKP}TU6sw@BvN_=+UWup6 zxqDJfPlxygtqI+X`Mn@m1%R=b-U4}|rRS$A@hStTpZB8IF;1(g+-nhRD1p&95l7u1m?fXASv1As7X~58?5y34L97pqq_dpWg{p{p?JlB(+;SIQ6Xm zwItL~S*dD;I%H4sQC^vcs2C45%a5B}(q)hbKf+gup^OEBP5@aGO^+V|KKplj8k@TR zvo@|k`bT*@tXI~%`q5IK2wa`0N>)5GlUD{^X0-}%b9nL7rv5RXB_H~6AijPye{%g+ zK}qgGYb;5oZPw1G(F>N*i8DW;Q)aBU#+21`a;MpJa)()TVuzRMQ*D!JyOvS3M$5K* z%+@!+`Ru=m+i+xd|GD5&<bY9xcB2G06e5ulvhFmMD1-a(|Ji>8OSUw7A0oVhqlf#w8y#H zH;LJZK}*1)%B6=VW6Y9}SS9JP29GFNDVM#j0=ymN?Wde?t6~9}Jh6xMt>I!qwcRu5 z!o+R#K<{G)0t<_Stj<-jd+6PJraRlzX8*>~SEUYh9_1W%yx)WGZ=GZ*>~6f;y(U>Q zY&PFs@U{x@$SPiZ!3-UL9JCNOrHcZka_wBtALvu`XzNerQHSTVdc;Yo zMg;_3mHpNI`Xozj!XUwW$c#Sm%pi;RDSJ{y-T;+^cSaym#+`Zn_eBI-o}DJl`q-VU z-rvyde#iOm*?o@Foc;yur}`cHncY|YPR%3o83{K-`t$45gZJ^2BzH~*XHv`1yEuo1 zOg5Yqv!8X86)|hLDhl-N=Qz;Dfd-EBT84=y*Kv#);3G|*aMyth`XC z{xI1*PF&u_Gq5HD_XIq^VQ^I;h-V+geo2uE;@%xI>B- z%=7g8;>+|z`dNBy=g;)p7thnHTW`>BCVWSKKKvUO$aVI40TaRxtFF@T-@Qbyd~uzg zneziLkFsLL9K*``eeLjlwDj`0Gg4^P)|{*GDgk&t)`>_BYvB(X)KbQG8m{b27B4RK zM-|~dO`qn5IDrE~!OOQN_oQS)ytv?G8RYne`?mj!u6`!VDFCKQuJ8GoUfytxUjFnN zJ^k`|dT!w*njLwZUfINmop+J`cIrR$>gMb8e=hwmEl52}FMfQLt1So#E8)hzU+CfJ zle|oR+$pZcvU`7XGpBa}eQV(Q-$P>Bi~>h0S1z|6iYA3fb*fcmKSOPEqF&)gZ2s5- zZ0TBr^=@Dhwfsy6N?sd8m9G*57DN^18onR@wL<_$<4@CHj{HV{Sb3HHobxL^GxrDj zW9Cov;+h}nmCvu!6ZUiT(%K)ns?mtX_usvCfeQ@k33;Ky&Mmsg)eu_&ocyg_chj+L z?Q_75%8|*5SK*V)oT@TXISKb#>MHL=uiyriyQXR8+Z1|i7%hS|y&5tD3*w}<^~4?j zj7T2(ll7yS0Z$MY!0@VJSKu@D9rEyPf@yte#(;O-1b|tK8C@{>3|BP}!ilNpXl~RA zuHTPOJj2z>$&7Qn%#qk{>FHS)SpKv8bJ&h=XM9gjy>y=DJW=o`Bmd_1?)&Mh&F8HF zC!8DcC~A?Z9(^KGXk}P-9IZ7}mJi>|)de@oJ_rF3H6muF^G)}TJOMkdZR>LZ;wc$C zk>Un?2I1nRQR?n&*H**mLFi}~J5WUUGrM=eAG>-SqVrq6vyE}lh;t&iu{drlx}-?8 zDsX$i7FrW!W$Yfmga9C5`yfW#u{62fQdB6_xGsICi_R4-JXG?t>07Oq>xSy zFbIQt6ElBNtHt}vgp_Fk4$2EI#Dng|l*A1RVQ^|QG*uQyZ}HnOYrv(YCBvyarju_k z_x2M#TgTF^JzP8@zj4gr?Lg9Ur!K12p~@2VD2i;S>^Fvoo_xjA=M|~4eb3}@J+$cH&Xbf+#bKTqJ^KYc{{V3 z&4IziD+eAZC&rHLnqntkOF{_M%VP;*B{a0njC@tuGVZ@TsM7uJ{&2zt^)pP^GOhKWZJnGl>}p1MTT-IanYbrVy+4D;9ISKJZgp z?J79cs>jDxQNo`r2`cingbu+CD<{>cd@uX?AVVWtLgyrTW$bbOMsjRlI6NgEUDjej zn)m~s=o?lnE*dOL9iHM_UFGJC-LOb{3OiV$bb5D4DA?|UFuD6lrr7TVBD-t)hGJmr0u-e;~Yyk-ydjrS3K6`mbtebB2MZwZ}8lvs{0 zOj7e_|;MK?rXNswte2(Aak0-H+Nv zcSurRdtxbB8gqzz7JrqI#SggoY}MZN=3FU8Xg9jyb>Tt)+~A!_H^~=cu8?Ihhfp`# zKwD@NZSy{$FHYjo#}cFK14)`7%5s0Ya+#XUx7Jog0}#XTu+pk_pOBI56KyX<&Q{(S zv{G3dok>>4=8$!xz9k#tFOVG64xPt z*Tw2oNypnt!g!l#Tln-fSZ z3?4HNIv0eLW8qvS{y;N)Z>R^cLLeBZEUXj|3gFVK2K*o2Ax|A%24ztP>O!4D#h?we zg*N|R6K|DV1%kbnG7^YGl#QDPHSWBd>uZqe3Vep|kOz4M*(i%TZWCUJ1kyC20OGwC kf>c*%pKJNFazUN`4{TG81sXU_8vpL`@I>YUD9NTnmkCW8or$VqGa zAVA{76m7h<4C1=UYvk@*lZ|&KL+`zO`T~znvWxCY-&F6rrjW5m(O6|C;~#qV+4=sO z33H$k`G0y8Pss-y!C+RjC8_bt=yuEHD+`fa1`e4r+EA#^Q$L|->WMz&OrPXPpDZL8 zzycMq510l(W)bXL;6S4I-PmTEdg!Y>pC-R+BJzxOrZLu8?H`17d-d>SabOO;XeQVK z04gsc2m;HVria2tSwI-P>+n0}<5>)Nh*oq5Tz$^q(a@}$P;wIuNrM$56la|!y6~!G zjJ2J@Hczhu)hqepEFiKj0NlF{3t_XntZNLy!sE~w{Lt3d!d~cj{)w$rkr*4sG2WWJ z?T47eOEaBSz`v`U=I@i^kW9{thj^ka{$;fFs>?&Ev8LcZzrHZ| zm{~XC-#NVLIc$S~9h$ZM%8~QCKGd4w5$t?{Nx^xG2VIP0c_{yQ(v4rFSg+VCyurV9|_lj?zaW*#Ur)o&4f(N zJUr%hYJpa)pseB_R&XO|z0X8PSy}f;|4|X+C9kJw0-mgdZDIgLMWeec^^ckC))Uj! zT^8A3%Z7O3$-VT%q0@~3uspln$3jA0Q4XLi*JUvXWpv{~kdu;R@dGX<5`XG^C??zLm-E zgD=%H*>UTr7wADf-bEEi6^&x>M89A%GPK@#;|eCOEVi^#2YS6KhLgbL`?B>%=3iFs zPl_{hh7m7N$I?SZ$>Qn6q8^w5Nr3s(J-%@ zafTI41|}=yv8G5&ayPmF_X9(-ve$n~KMVM!r=Hekx!n4}&9X#gM;Q3AyN{}9`~|k{ zx4dBDwP2zLOi^2NUybYA7*k0;G`(be4pp7Ly3TCYAB~QJ7zfblI`dfTrX< zRy@%f`u36sS)5Z47u7#vtZHyoZ66GExKJV!Yi$KMXd2A$H2?5G2~u3a*xsG^4w;3< z01lh*Uh&H_ab;ZE=P>M$Teo~REJzK9#bbiZW)2Ky&1#UHZMr0J!FB_qSFA~I+UHgG z*jDac5=DUh>5Kgz_b1LJrto>D3a5zNh!>~qey*I+?EtyxUF%6rpPvTo7IzqNB?rh- z852zeL%G;u{3>rZr)+cybBjh9yyB{RmJs>y1Hlt;6IaP=cPyyC?j<4dZ|rl$z(TCw z=Hl*%WWE;tA0=3zRPo%`B&eR6{7vE>fFnT=|LB6oz6?`W8t?9o8r45RNA4FA^nqRz zeZ5(1upO2r{3Ru$;v#`=VBiK#GLxmf1Mi-3#RxkpPPZ%*d@&IX5@zU}8OZ?=c`r2< zc)D}4j{kVPAnv@_vIk~qv>fPtBm7R>VmNWIh!;-=z;^D0`Yd~IE}qVnd2?hkR$M7A z^1rvj0IXMt*j~zSQVC(@+~-T0uBsT(A~)$-92z#nt18lGNXW*w)9W_=)-nDg6z8Ij zrAR?_#VYQK^q7bh9Xl_+)SMXnZp4p0kTZ;jzWRgnmS;AQJZwHM#+`tZXL%-p)7MSk zwmu~tKk%HSHFHp?xNgT~E+i=}0Ki06(>MN-PPF!na;8}EF_nds1W>IFxW@+Dr~BEl zux*QnZl`;s3bU5TtnQ7A8{|4KYWNsuUclPZiBg;&>V+O+5qJxBGf9_`@9ju;4)a;{ zbTu{utL;^>q+7Gs2OhJ#>a$geEdSf?SkHIxt(Gb;9Q_?qEIgQq6uJM7-I+MRPf&s- z`exL`D9>}iY=;uL^;YjL3Jfrb9u&UBN8;D_kEeFOa!=IL4|XS3iPT)>Ana;vQ8o6`k;qwfQ5=#`3rdXmgQ8$oDpu0{X>%5JH?j z4-3@oj8zlR0)Ogp7KqDKYZv?BxqmU~y6N}3fp~t zl^_8v4$gg3$IN1#F}zjpWK02of8R^(^RtAi$1AAqRLO7Rj{NmfCH!o#prb;8$J*Iq z#WDhMkZ_I78&L*CPDcIKkdm9pDRD?yQrE3u2Rtz+*;ns7WUat;c_h;GV~eb2PCg8n zD^Iu!t1T85EWMncu|GTmL>((c^b~E5NF9Iq3Ibnr>nfb@{t~v8gJ)vOaHNpLO1?(%LVWh64z(K!Y>l)SHp;aIlm%xMhI@6NkNML>%l)}=X zS*~45V|RJ)kD8MQebKzAnokrkq+FQ7Cd@nHU;#t{9*bY`++MYHg+?R@#wT>I+%+`= z;h6u4xaa!Q@2T;k!YZg%jgdT%n?Ffgleo#!wz&x?B&4)*1&I=Kb)06tL#6`6#KMYx zsA7bFAFw4&T|@$Ve1`>CK^wGd0jlIIeY0e+B6WuirdzSb^n;7-69g+V0mrcJbC zFwyZ+s&G5x=OBb7aW96kkJL4;@;ro=mhh=<%dODee9m5+WXhjA8Ai82b8__!3+U5@ zrkPQRmljBYp5eiwTxQVf`4qMkjby6vvvdfsLow{p)VPICg$!3{>?=|Ort5Oi(Z!T? zR?r@Ocw-YUXAoOYub~`AfCk4oNTSEK+d!{!;^MxXxrh2wOB&hDsrIZwt3kX2C7Rxq zIeCN?k@IBz#i*ys6I$$);F@i*9lL1OXMlO+*5U)0Ef-uS_h9CQ5K!@R%owZafNptt zA{OX*z@e?A%4kPCUuoJN8-pg}P?&y5$DkOAj3F~G~thwB1!V8?yaqi?q za|KMgjO^eKsHj$G$X^X>$FoAnwLa}mD$%Gnc^P22TtI7st0258+W5lK@pHr*MtA20 z>q9FZY$!)MzRNa*>OOksBMJ$g=avhOx$@OBVzNs$HePHey>lMmwDhdxAdq3Lg6t*v zEMxyR29gG80CJWFZ9lm2)nbri5VS)z-^#EaBx0yjejm`};1g?oUzFd}$?b~Ard*QR zjk(mnlEjXzSlXOy$G3QtBB@HdGoZcdRkSUVhHwiim4+?!xK_OQKS+gYvm zHYus)?e+M{qf zJ*5f!kp4Dx>K{@^(*Ov8pOCk2GS!p{Th0D2jpmuVaqnW zZqAJbN+$wHCvT!{Bc#R{y~&0T+hvqlI&|QoWCviG=M{^SVKOONaG!lV@R+3|`S}NL zAnN7=0~Akt1~Qd{7uDo`eZiRf>=`T55OXJXtceC7eb|RO1R(m7=24hRF+^h-2n!K~JauCHlb=}{GnS#{zFwcqBO3ILOZM>8x#3R{;X6&> z7z^n^YpQ=t1lkrjGtx72sBj%`~hx?){6m;4d6J#P^r6bZ97>)Pu?J=!4bh(n@;p z%S=O64c#Z6y1m}t94meV0Z}29Mz<|xZyh{{+5!7BN~QWw-t!c}9?6XbK$p+`*RQ&D z)=SUTIr(MdvuyodO;WKltX_7kMr7yyCAEZ@>`!Ut+vmWHsC4iO`LxdBBJv z0x&k+3Ck#|+sIb(d((Woel?cEYG293VIFQ$U-qvtDa#e*4i4YUcGHU$?0R?gyOP zYqpOUx#50gDaTcrg`^?)dEJqZtepx4^!y0HS6_#SkFl9mMr$2KH!(s%xT}&}3D~&y zLl)ZzwA-a*HAXvYdYk$YI#3Ic+$9bLNM1~W^tMG`C5uzjqL7OyP5ebOU!>fV?{|b6 zYFo`!fdZ;uY9f{ZbMaLuv zAT{x=<>oQLg80-q2*PH00BU4=9d$jZXiTsd@^cGv=UC_HG8Rjhizp;qg?5n!`t|k= z&7REN3T&PBeJSC86Cl?GFy~P@_glsuRx3mWed=t4(w8rrhYm&S=ovbEV1dlV{$PNd zAwX_No?rtT7uXCfuO`~q6|=>z9RVDaM4mQDapuiaQn)JrI7S%iexy1A4iKZ)EfptYjrXGex zF5A+B`;OXbJ(q)v1{62~u5pz3KW;T~o{eaHAoo`46^hQGO8v4OX2Y;F(hI-zPL5;m z3QI{?`;VYQ&IAX%znVG&t9EDoN2k0A%VjV}jRr?EetEb{hqk#>S%vd;LaE5|=17r6 zjC!9M5SJa3{Vv94Ej1pUb>J5vNX>nSYye7@ z=%QxcHWU)-lTCl+Q_&bOgxABM~3hPtu zg-XZ(=|$Q1+&XF)%!`(EgdoDZuWVl25f6#ZGsGH2R*$CgQo? zJr%G0r@g|ArhT$9T+urHJPV^DDP%muleVvzDc2$KfpFG>jKDE|Z3L@xotUtKuz+Kf*{@hb`$q{w) zr+&#{`;W{rFGb;6uZpHrqm@4upYv?TI5XvW^0(QpDNz&#u;_4yvG?^?z>kZ$YqI*|V2rDN&XV zUy&@@4tz6hR3tR&_xG;~bKyHC&K-&5+}@t?+K)pY^%mq#JExjVPVm)V<(HhS9UPn4 zwOvGrlPjO6Qa?(U?^|e~?<|=EzaAD7oTy_{({>jjX7prJahT1*@BPaOWiR2$T}P9b zUwu-^F1X1V@%H|0$8jkkilbe5{~?+fGIfA+n&1`Tsh2}HRq8}hman;w7K zK4`0YaqpjWI_%0N)x>s7^WcT6YCBx}+vOlhW0kFJr;Ozo*q?XL#CiH1`!jDrkC#-t zAmze3y~2f~<#@74a_GhU2h6fJ#ePRM^ZWhzaUtNNXNTLf>94kxYM4%$<}bfwPA13> zkr98t*CM-BrQn2H-BE|QQJ#?rBC~hUQ4rzHTZiyp{`L`(7>6g1`dUsahO;)vhimh3 zr0>k0Vy4a|*NOLmrW&02KPSH*tT+an0M8y7%H-hp~X|m~UpU-pL1n85}xT5wfEbC3}RA;ZnsoAi7A9D3*N$}KT zE2o!qU`T`?*Q6lFmG6`O9rR2pk0tQbO}xk1ul-WaC3R}U>qeJH*S9HX$9vwtwQW54 z^JgEeo=(B;&+eE?zU@WwqcF_IS$-G=bM3ar*Rcx1b`9FDjLz&>c&v3xqP6(qLS*%FPH;$E})!p8#m=V_2;-KZ}PWIO>kbSNc#Sgj5;^R2Q9 zuqW&5k*|_;b&i7nEg+RN{)4hkO#YcR&^nMuSp*^s-=7!eaiPr;hD81~s!WY-Bx}_ALlM`j5fu6!PLf~(H!^0ZGNG}7t5kUdC zY3o5(spV@yw5|bGuXjEfB|gmY_4ak}RbOpfOPEz0>-lu;jEv;9QFSKlv$hj8{m4EG zx53bHwp!Hr8`chHM<(?N8!6l9H#Kd@k)ZLVkJeP_Pt<)Pezu>5)fvm$?$40+Y^BzS zVM-H(-=9dm2n9g8#2ACQ0~?5cZ*DDJ23VG8owaOm<$$%Z*`d-4H+lKJ?&pwOuSsJg zUOWX=;lE>m6vQZRFJkf|scvfrQAd7z?+SH=_?r1P*m#GLQXx~Dux8T4FNC+Ry{}Zu z%c;NdCTF_Nh2x~oCPbA}pRCgO$KjyJDofSLjrNrnF-A8p#r!)NVh~NcdNJt=-!#8% zU0;ONU!vYZ)kA_|{Eu8GIF_o=Is^epwUSXJeC4wyLUT8-i%;uG+?R#-fpsO7!-MVR+^93kr3uBeh9rAy=b8s|~*9H#hd$bxArf7(Q0%Szqfh`nT#eXTb{n&86mLRhH_|m0tX?~?r zjB3?VVQ|jrTH*yGs&kk=FsM0GP~PiCxTjtgX==(nOg#*-N=Dj;_{;CGR5kR;v`~FV z_>%vsQ~gBUJ>2*@TF5@*Ys&PrLPrOo9By1Zeb(!qg{gjSilLg6H=NYB3a2XG;js03 z<94y#6KxoLTo_r~nQGqDEl`7S6xi_hJsqJ?yxp~LHJ+vfSx z`09%W>(rv+x0?hJ0X30`{LR7wdij>{lNIILSyS6C=U)EXm7M(gs1b3rtYjZ(M;xCi z&d5OaYS)cZMWVEYu`0v;?3=E`AR7@*lP-<94G5~$JM5L4AO~-BYqhw~Y);ah4XN(a z-_Ng4zI^xD@2V5@eKQ+T-h7l$`yZW||MtQ)O*}kvMQ18N6YKKwr6P^~qO$h0s$;k= zxKGs-ijAjHVh$aXiLvR|uo|f%%aTA`uY_~de61*$*b$nsiKFKIzM?kSK}GE zOMK1Ki6+TIA5qsCuHU!rB*?l&`9P9Fs`jnGrciQ8J(Ismm#$jZPEgHYavS*^b7)zD zsYmvia+SZHf!C!_w7~3J^J?~ccS;;~Q}$xvggk!upC&||yKN)FamZsef83R)V1I_v zreFGNj4mekj(&%zoA(@AI=;q zW%oT9r==DM7lZKYfv69k+B0N}$MT+u7?%97QjvT@>+yyDDmAemTsaX2TBVL{1^(7# zpjJF2{JE0^Sk3fZMyfWaRPpN<@cu!euKVg-?**qRdt9?UAKj79Tj~xoC7<_L;(kVi z*s%R+f}DVReNi6 z#Z+2KF3jvKd$G%7HOqgB?$hH)<^_q?dTVPQ-#hg9`?86(qbIxIgS1Kh&(1qH!3J$y z>=XM!Sm}P;T>)jp`{TbnLRH*VP%S!FLw&D>B7JetDZ7$eag4%wY8A#s*C`=gz0lqE zEA4`#NA>YS=|G)q!9S0a3$8g%4(7SpeM`o#%ADV55%Akrt#NwB2(7qV+TYOoOX5(`B zPdCanzK_nysgv5ZL*G@o?2e^)^!$7k#v^???~_gshidB8EY>h6vCGoZCZ@H@6X|d? zR1YIi!ofsJToCR0X35jW+>|%=Vpc2hy`Wpv{`b===76;vS9h<4PiJ^rdW}H|S`5EQ zEz7UqS%Axl;E!s}WpK~X<|n>rS{JiN@?YSI>B=r6tfdjVGO*czH5jd$x189(7gz zMl3tr@^V3Da!2nG?vGrQx5kq10NVafu=F?;7xFK-pA_)C+P74Zyw`1Jwj5m7wx z*}e2Emk}v2F>}O&btBT@n9gpR72^}L9ImjVIb|j)%{9b7y8Aj_&|FSk&c_^XYfFXE z&B4+kPVUnCiBz;o2$tTzZE!p1RJZA8R7G{gl6a>33HKRFr?foKOkj;i{Xafn|MpMo zpT7O!akpTA`lW!_6MdT}4_%(PtGW5RQ!g~~V0i_Q92g|8cu!7MO-JRf02<~pdNpJ{mK@wPOg9LX865QS0HTWXIU4jP*?r*>M@BMf+ zHPbaSeWtGU>3h2;TvJ^E6O9ZF002xSMOiIa8~5Ldf&{zn%Wt>98U!0DH7Nk7jYWSl zM}*zeSSo6%0RWf@03g8t@Bn)RIRF4pZU8tk0|4Pv03de#+4f!(_5#U5ML`yL{qM-> zEKURfCQBt*DIKrnznNc+2=o_zL03iKSt&|9 z|Ng4`8IX!KYc8xBddN*#ls`&^f?i(@_;KtQ( ziA*P=D308Q5j7l%9bZ!ygG{Z2<*%Yj!`^RYuCnyEX}T9mI+d%EbEEXK6E>OxuY9_f z0a^mDNoDuMIurZa0cqHO5lzO1-zK}non*ki9FZKTNo?qoY(3-_mGnrST!FSq3Id#vo?-5Oc`BlJ7 ztZ?axQE!6l)7&Mpl=DyB+KmcS_oDoSj)_`^l!?8^_X6F4W%uRPKq809uC$`b3_By) z02+wxUuLyeNx9BMwb|U|BT+j@)nuecVWhXS!ck2}8S(u^+4j^MQ!T=3(2#4*aF8zt)0rm{WiWE3@Nl2S*rf^T?zHW!+=Vb+%! z2G<=_OmSDJ>hT~eu1k2^rhl5@IM>o0?X}7uzS*$4sV|MnRM@|KtR_dE&5fR9$-`W% z@Lns4*f2#C;wonsDhz2&peen3u25d;Y*`dM%&&{h%j0D(jV3AM4`Q{=ID(rvR%>~O zCW_aM>XxvaBrIQ)Pf#7y6UY9ELiZ9oqUE}S445q6s+^)1qcI$9fy1`?C(qar!(>#N zv^Mylc8#I?2g6Bru$TzlLBNFE7;PJ^p3Pq>-c?tCw=80ORp$wP;;zQtU&_wX;1Vqm z0MdLvmThYgTnZ<9*YYMS@(~PjXwyEey}5edE0hUQ?Q$v=sI!+qq@~g+BvGip?~e4! zjKI|2-=kZ?kFwg)VHg{t9nQ{;A)(ru?!KCu_uD#3kTGt@ zGOb6Gf@`ykYrULnt(5D0pOI6@t=A%YHqS6&+NQr^iiYGrPnwy;X~L~qv<}>i+$S8# zE6F)MV!OJE69&FAty5OTiI8x$XYBJquY%D2cy_Yr@rHF2nKj(D#sw@K`Via;)GqeW;Ep{nezr0Kp0?265fw0G*+@6l7?u zx2DI|(wH(XZ7bU9-m9S40)u>J1|QnHo-;J3V&ri8{VG_n0&Eh40(I}iviLxkkSR4h zfF*r!%n?W>%=${IEJb%X(hJJc^zQtx zUNiXdr>OtQ7feMNg`K`&c#Ab0d?iyShGX|3Z=E*pF#PMLVzB$%cf}p)Sv(l%Rn4(+ z3R^S{jR9FY9`%?jY_kkboy8EZ}L;H06TOK9~(Te%oJtt`%OP-#Pv^uIiG@-C~WG0G~T!&LD<+IAY82 zC>`gR#*~Bt*BMW`NBcYI3NC?z7r&i;ja=)crl|EZbjSo!;@lYBzGz^eY*V7#D#LYN zz(ivxqV(pWHEAc$YmxTKUD~?sEpOy6l=K~gB!AxA-v|Z2^5RY~<@bW#533%n;i5I7#6)@*_BLZ66A4rW4EDVzWJqHSFyyL=3+yB`0AtqFe_oXxL zX~V1FS3aw?IGfD3D@CD%PdkX!hFH*nJ1|Oj`)VWI3!<8e?2W;11z>2Am95OTOe=l3l}7lleL9XuEA+wv}1_Hk6KUO7|smIUha&TOKtrb!dIX zomCzfX-~ZJzV;v}n+IpyY?@ACAkJ9g+JGQ>T~v7X=olkfT^~)>WWFHL?i|x%|8)Kv zqNL))`^K_P6)}N+3l`>_^?{S)NBOVe1_G~vCQ%A*+#WoV7l;_ibm^@s;l`ttK3&-O zTITNr61!mLz9V-oY7gYcyLUt4%@+-s$5C7SQZHj&z&vx-hrxGWwilljE_E}O<_AE& z6^J{23gMKlzjObCW*piVe-Y2jotCz)7#pmgoic}hAzeL#oFDi8?myStD)1O+r{jzc zPlxFMo2=qVcGCJ_3#(S?g=rKkrY}FH$)>5&zJ8|{!*@7JoL6`wLLIuBK_fT#oi{gO zq-QHkPp9xn8^id%ZM=X_+s8qIW4cld=H9ZDd7c+}zk*-%uYs>VD^!xJapi#x*tuz= z8lG6jrKrOL*wa*5{_4`lL5F?E5=n(jG;8D?gh1HVEI8U#oP1xmCg_LJ*3VMeI_J6@ z!VlD68AO7jKIsO3UMxglFrARy%QPPzz?7<^m29r5!u?&Gce#YA%_gad&KWbsKIoj; z3C-m;cUJibt*0MGP*pn1+qZnvwvX}BMtG$@2=kJh#PveV!hQ8 zh~-y_XKwduS~qQHiKxmm69iozVG0dYMwMcJ{ETGEDH|c0o)YHaa&F%5l?(S5|4*U& zH1|8W+=@i55Bq5n<4I1*dwvP@xS}gc_VVczr5QxRRN*HN&5uVL=!8}#;m02T#7WVt zERd8!If==8F@Mxg$_OHam8(Sm8vYLXC*#5ULHORaR*uOz?1@~na6JFVcKm5p>3)=| z_U)2Ldq|ZhbsfE8yDq+;Fe=~S9|~LfElUqSXw-~?1g&aDS?5<~1|J!Za&$_pNAh)2 z=i=@z+FWU+;gA-R%nt%Hcp-BBTFh&Mpb_T8>+Pd*4oO30hVu3KGKmb-Q?~AA;S6X# zORb`yh8i3I=PY{$ZSc+#R_Q|IqN2xBl_S>7`g0UKTn*$ZP17KHZhc zflu>^YC?x*i8uifXbe8`@ZKAi{o9`vb79!{|h?y^4JJGXMFo7Nm$B;oZ&2X#;3D@36QN% z8fL(nJ4I4UV{7J(zVc$2c~v8+@*%-rAE+bV7LPN29MOTz+t~$P*1oIpRMFg^f^uSE zA!Kt%eM73rTSI@f17C_64sCq^V583II~j|zX2?p(^fQiHVAZBUmnSLMHqr+2?7}~c zAiyR%@5ndx$=>`hI?XU72IVw8aWhT&^u`$8=uAk&W%^M@OCD^~c-stfwq^j}xf=wP z$iwxRK9d-?`r3Z;SIHSo{rhA)p2s}qd?o_XT8$V7V_|i$q>UH3<8*cvz?1tNj46O= zwtETm10F1(v|8hW&S#{6R^d0SjFY#7E8B7byc6=#4VD{g$%LahrPAqA&9_%+j>TE-BejP}d z;5$vDkSnC#{06-u{67zzpvoWoF%Xo@?}*EaPF~oK&y{SWKgR%mBS}oTaoI<|gdfhP zTC@7&UdPVUM!_b&cb4M_U~&&h2Qo>1vG)canQFrcm&88a?6NLV`y$JWG$R0c#@TV3 zWp7R}+=}gAT8%(zL@_Bbzo?@HKm}*+>LjeK%>wrx5fs=AD2MsDTxPx8EZm%6% zjv<)Av4S(&GU2dYUxdLYNDVj?iSpx-ahpZ@ew(~;1ZE{WtefTw2FD3Xe*lJ~e#R)p ziTTBs%93nq-6&z4Rb-2~r~;j3VvIM@R!=^o(1FPbxql-CA%IyCrDR{^_+e{HavkDT zDT&=PU;oPH@sb0BWU*!Z#WCxWZISkm?V{2QpvOoB@=yb4W|*kn>2XS0DQI5&VJZX> zNDQ2VbC}?gvcgntC6w+Zp_9o!!V8({)&!rqf#1`d$!GBF!0%kU@cmCVW)93sL+1Yi zcA|-B>Ldsb?}eTX7}*u*(B%~uHn1k+#rQP7OpL-}Fg}`B>z;uI=m|9LZ)1>&rzg+B zmRI7`XM*uC5e>!|st+gEoiXDv>2L*fZ zm7P2vp35krB5$%s6Hg07Fr}3nEMBz@TE=Bq>u9#f6NUU1P4KHf>*3ySn&G>IlcZp{ zUxab^Ssf^XoKCF9%|}}mu*05;M>+fu_mk`=QqI}_6RNiG*MzJY9Dp|{cBrA)a5P|- z2)7aLjo36oT<(cu#cn#!zZb>(!2ucbSelmN&>Mi`+C9(yrF0$SR$H1{=BS4FMnI}< z(DQ|xvTS+_O7putLld19ohDbu35)u%Rg3VodL8ty<}Jv|Eb(Wu1_>Q?4*eFFnfeA5 zV20&SIZ*(5I3szR2}CT=1*ZXJV1N=cpo&s5(1$nlHdI7A`CqsrBx!~?rid{W;N7gM zPM~$ns`iCog^Gn7uC;?8-b?spWRQgKLR!iHa!=SDdr?M&<5G86&K$CA-7G%X`>rf#CXIpDIU26a7E~SkdUZ$vI?z%83#dPd_K)0F)eD zC6$&6?YDWM14fHSN<$*VO|JY2g!tDMgzwM5pPuy9ld?t^1!Z1zh9-N;fhi-LY0bSO z3^=D8B7nc($F{Z=yoXuLZeGB@Qdz;U&fDxO8%7V<`AK3fs54u=zj*F0&TRSWGS1Pbu?!Jh1Z15DvKB z1$2HmW2Kg8Yj^5sCzf+0aLlR#haX>ksc2fmS?*n9(J{tS(6I zNez_QVHun_JY-hHU-9KDRK>x6H4m3`TRh?n1L32 zZIo-{lI?=1$4Dj!aZ>?7q zNc3&-Z3<_`lTCqWYtG)~Alj82V2g;ETh#>Drl}~x-vkG-w$eDKTLSC^`%{+b+kVg| zxL9WZGjoq8KN+7f5>6@OhOtfWuRJgkuoWucAw^~jHUA4ne)qN!fDBzpboJY&&}rO= zhm)dJHknvTKf=&9z}ph*oZ z$0aSQW_CI<^5qIt-1MT?dc)=O0HXpO-;DOdBU0<=>S!VN zBVD%IBmjK8Sk4%mlaT8tVQ#A-+MQ(y=hvq7f!d~*;;_!*t#mp`vx5dNqA6WN|+`+6__vMaS1s ze9aP=S)$0+SRAY+t=;NoG?UuRh{JBAfrR}s6 z3^11EngV!@jQ7FTNtxc$qRt?SJjvTn&(eLtlSK6!}x^?#hX7nb<%YQIU2M0aI+Au*m zjN^v_30bi|xhl$BqG5yBz4dDj#5W`UP08_nJJ)~RRz)9mLISmCy0WqY#lBX^HnE2d zFki;GSpVr=IZXs|7#nI>o4q(uaS$QGQ!VQ5Kux{~{8v;H=`P|tSdk}-&E7~fpn7Kf zrxQD4#PWyB56e1J`NF>Z+8s;2X`jcV1s}R(-dI@W`f!nY2K1J26umq@!$Fm9ZRsTyRhlL{YNG`B%7Q zO5-Q++2!>(GxxYR2GD@l8Efc0czN6rhuGlw8xA-**n>cW^)M(qG~=(b{HgV5MZV}H z-G-YqjFoIoF9v!))~YW*>32fI-!M>PyrKaoIdc6efLSirg%Mbp3uhTkSn!fTq2GnL zlJ>WU`$jVcwuT~~GDJOUWWYp3ud~iE%{lFXO^E*x1XONfmJdBBu8pZ5g4kuXoJO{% z(bDnQx`mC^IKb1K9S1&3Z$Qe|BfYnG@Pi;>k_fBQk7w|Z%G^$k&m0Jv)b{-uz*N>L znkY*!?@LH8*G!=ogHK@mN&2VrKhti}W4oVU3nJywHOe%ws7QarTyrwe>Rxc!Dzn$I zF&hC8tnJ1M7nHx34^iXGd8EK%kHJ?{F#mSAl+03ympOUBnR?$THPsD@F=g<+GX)wS z8bp}l;h>S2)8Sw2-B`LQkH{{#cf+}&BeGd=?H76Zsm1APHjk4PXVJqhN_B>xnpgH~ zte3;AA1sJ_8_eIOQu<=LRCGm3aCHa0i|HI zv)<=*yD_AhdyW~|-O|7B>B;v$QOC>|E-tQ_?+%vneZB5^#5bp0&6Lc`bjHbG6Btult+bC9?i@!%{H`{Fh~U@PB2 zgWF*=HuWem2%Gn{_r$Ds_Nil=Q@rbfpdQYDz~=U4aOLCaPI550m*t-|IEcT-3;CRxalp=GXZzG&4=!%6u8nsI%`@GLVk_ zg5%!66el*qtTFehogD{_^M~z^cPH!JUY@-Z`al~6n3DSW4xxJBISP9E9I0_8+OHrX z+RNN6$^jhY9^t;!-7Ew`JKHvieM|g<-%7NHYMhD7%<w~W=A)%{=m%)jxZe?zRMNk9es z{XT^k$AX<`aV}WvtFGAD629MJ1HchbYSCXCiLZS+rl6d$+yx#dSx%G3O(yZyYYdn< zw*QL-uI#6XrQU`AltBcZy>CD-ZO30h7#kEFwMU#UnGe`70Fn6XKB5U0@s;j;67|K< zXnTm{{jOQMi?e^*8R6>uV}>ubOEwPR3Msl?hLi04PvqPRL4Y6bxa|wDIm}5_A50ZV zQ1-J;r&ML-Rz4C(uSh?AS!GH|fexeTXWRZ{ay5{|bBgwocuC^o(i$r<{)iZ0=uS*+ z>=(F1yKpfb8yiyN@#OM!d$#XolR=YWTKvu5cc0i7{7AaK4yOJOqv0$*cpT|s{!n7% z8+Q@A89o0R5s(=W7Vr}cIkc>@_5U^IvBAdm_4XdiGBfEF9#FXWAi&CNJ}Svb8Ip^U zgZf*b86KkUF|o38IP&H>xXuF+mhUk(PbTtX`vi+;#-%M%j?8@S+rv_zkIRZo#MUi} zBb~vYR=f$NTjHf8XJOvH&mFW`<70k?bo)OdTdO}wog>h@U)&Ji@-S~Rcho<>?Rb68 z_Iiwb^kO`y@SBs~6`>PRF;~o9A!0*e~Jw#H;MX zO~H_!{P+22B8L)d$9t2k{LvD)Y-pAQ&_}*$ zg5r$pgS-8B5l8yo56fc8THCu=$$JoW)hgQzI&RtGg|e&w*y>3fKLe~ z2hdZ-E2D2`5*EgI{-D{d=K#0@fPS5~J*!$cA)5XJxaMTF7e=X3Ri6ldI=Ol^8>aH| zk11}B_G0hxYkCBTzo*ebl!*X3|wG&vJUl+A2-j6RP@%Ke@ z8J)6TCxcOAb#%q7SE%uX0Ds7k-d`Yx{@3SIkGVT5FoV%7rcK<4zMCsn!s`Pq{#5x4 zLUYJR=AYtM#-d`Pvs1Ke5^J}kV}8#u+tRm%%n z5IDZF7OzAWV|B~UFF_a!OOFPkdzmjzP#UGA_0nB9PmkPzL?hzAdqR_#07d0^cE*#B~9xbgpIc z7XgL;FCCQ})yA$l^OM?TK=qeCpNDs}I~(}0*)iVnXTBUpn&kpUb!cLu$SaR;f4#b}~C z%Jw?isfI}V+0&u(WI7^Za)`h|@K?&DG}>0r-mFp6HCU9e=G51grCU4RAknvb{r=qa z$>I?w zJ?03{8!fXQreQdGI-?zAmU((E&_t!|N89gXx067cnFelrRZf|RCLxB|2gPVOUQ=maN4!tn0do` z--`>Uo}Kw~#d4UXelxEyS~MbEbc-_rz|lLpDzy{6xQ7XjzYTRMTCo;|ZKR+JNP3zC zF^O*OBI;f$YRJ<*TRh18o1Ch2tbbS!G5J*?1~d@-Ql(FEAP_A)%v;`O|6Gr^KAe1G z_cno(sx|Z!#aCWKmawX%LOoY)Vn1>}_)3R=rrJi_k~n%%p>rjgb?}%U|5w=;&jk^; z&iGb?c3TXXC=|<5@aQ&xD=dtoAKJRqenbgNHbZ??W51)5W~2uge(KL)Qnk|yV^W}b zw%@b7>3@%08arl%OJQScwuP}brG6zGJ;=jbU23B6(M3li>a4P7v_kB6GmKG*V|G?; zO&_N{dTS=G++ROlnU$RTejzk9P~f#sCE!B$FHR?B{j&j;dT4#+c&LIBEsyy}p~RrW zz_|Uo4xibN(mG;9ovdiBi6?*@0fTf;0Opiv1q*V6YqR{v=TKvw7q}%_aG}bD;+(I! zq9)6wUCQ>SLb&{q0{$u3)~8yGWF6l*PrJnvqF~>nU(+{TC|s<>g-R@%n8Oue zALZLGRp_NupL9Rir80rOeqqspqBf=dFwfcw1^sEkiHi6J`l4bI=FEJ zNAwe&zs40$AZBRCwx{$HoWd*393FD>x5fP#JixJMW*nEyKq$!Yo4(tQ&}3$#gLoYJ z0hzjYw&&Fj&#z&m*Ye%t9^@;@Fiq2UgdC9Mbj=pte4LsYH7|q`ZsITESchpOQa}K4 zgq8jjymip4Zkyvc&*)>t&QrT=&6f$}@T{R|*l6X(?~E=?qX!nl_NQb;nG=7N(!`Aq z)cG0n@Y!)rA}$g2=p3Y>LAHge*sksQ5-xAiEa@Ku6p8Pg3nCTHirIb@$3dBF_{g6F z=uFky<(i-^8efoJq`n}&$bCV6mV>oYU0L4Qjv4-uO9ZZ&j;TM@^rJO$Rpif#(S6_6 zy@x0I{9`1ID-0MpzL6j>`w9#;kQFI zz!rUP6G*YnM93NEZ!2ZcIn4__w=!%ZU#ajnoo~7~TP_X3_H+@5YHKEw7il~}kO3 zZZEz27$L1avy$4wqm_CT7SjBXC)7w3Vvjye-$2CGgb6GL!n&H22ieBtdE#uj;(%%i zH)i#oqTUWd9hv+UWG+N12R6(Ciz51D2P22Vw;P%av}*~j6Ok(9Eo^Iv5S_Gfzvs>C zJY_fDEz8SEma_R5h1^l@ZGWv}>`O#VXT{QaU&3#e7Eaue;&tH=X_C)B`_!FsM^#Uk zLl82Epo;mejKQb?!B(!aVIk@1C)w90387Li_sMBeYS<5Gj2?0Z9#-ZapM)*lKEWD* zmz$T5gNK`gS3rm7oiML}Ft-33H@7f1Hwp+n?f(FrT&(P@z5joJw^2PK3;-y}smoSL Hn+5(48$(9;4}ej3Y30ADojw~TIqKvhW$NB7PF{|nsLH8%o*LPSBJsAv#~0$hq(27!=r zAP~s~1X9Tafw&&OYcNv-251omceO#MXTR^8OW%M%QXG2Pw=9Fk)+d9*9ITE{w@d=R z>VM??k`}P|E~mukX>Zob&4OW(U*`Iv-nXc!ehJe5v_9tmx)aG6`0X4SJ1r|1=4u88 z-@0hZYDy!SX81eCE!AIdU(vgv*mmjYB(s3k&R|?s&rP&qY5oZxOH8VNPxF4$(z{Sj zHb9*J|MUOSz|C)4O>Bs!C+xWo2}D19v@934MHGH|vkd20c9LsDt^@v{n= zp(nXR`|6KCoZSAN^ZarjZX$!g8QBk~<1^c(gPy zqsrqGv_?B2!l~>;vx>CCZdHovZ-4dEMTP}5!&I{T07mkFk@mBa$E0;?;>~ii`t+Jb zl0zDc@#w)EstU)1F=_Zv{8ogBj-#}hZ}vs5q=h+f>&<0b0-_DSqw_PZk`02>ONh`) z7#nS$P3!(_QAQ~`AgN@rE6oK96*q-lRaXT446GWF6PMu=PchX!>hsznn-G?X^6K2y zE*~7rFe!03E3X4a*wl4Yw2mtk6DXNWzx_2QR`iwCQk+>WYKZ9^)aIWoTsh8aUr*ag zSDQo6kXPDDK01dQ^z`-8>tyjU2t>s;0e7d&>Dr9es%Gq{AdIw%xUGPfHE&X-j&fin z|Ii)G5rsQSz>mch&cEmz3mlcvQI~>8-VzLH(2j5aElC}Pvox1?C?wn`SeK$IaAG<` zZf>h==5%lsJo*k|C^FuRKZRW}Pk=gV>S8<-1dy&mmd*LpFu~5|Jw%@J@SzzJuo407 zj2c=(ZT&c}00j%gV;<;+h~7J}Knw{XhF*1PxoBNg{a}#baS^)#!IrZ}HWl8b1$|>o zT|N&i%k|1?A7y3~ZB$`$U?T)S)(Z=dNlWDEmZk^A7D|ORK*_2w46Is}7CaE>`TC@$z+;4p~O2FMVe3HonZEdv>xs4~Wpw73Xyfuqd2v)bhR z?xB%6&|_*8T2P!T07qx~{#R$Le(_a7T!TTv*jPpN>hDqkWt7#(E-}N`(#TOHY-~a zD7(3SukEiq%%faei*0X#;eGLgx`R9VWexfX+4)&jmv-X`JMt*K%b@MjD{A^-NJqDZ zkU&}75&(bdQf%suf(2%R@|nMSsr)8V%$4qawn0|g)F{m;HbhOyMIAjlN;r83Z7v}W zU;Yp)w2H@Ih1t0YPxhM;$(i$sY3Ti1TV%i3L7r;LEdb6i>^C>!U#n}C;pD5+&3vUZ zCq;miP&j*@#yTG=^oQf%Rx~2V#&v^h(nh0o{VqyvMzy2L^p! zE$(Y1pr`kLN!>2u%ZSU1O;(&=1IkG9(5`dhlZX0ku&@yK=#&fi#I6&#L#)o;T4pjSf#K6!VOxAlaP zvq(c)=Tqlg;x?Jx;;u!FZp*&8wL7z@`iQ!GVBGo0JJ<0STQwNju~T9?&B+8ko*MIj z=OrFj1O?r+Y|3zAt;f61ZO9t4T`-V(T1*n8D5B{!gG5)~N&A{#YVDKmADT5Nm~>&zJBJA`7&m%Nhb2Nn>V z;}WkTO-aRA+lR30YL=5fPci<|(3c9(Zkclbkt3*cEX3gcLtR`aV{3+rIu~~L)p_T) z1Qd^nf$fNHB+g4YM8TC`L?u}3G{WqH5qlATw^r0TYmufHfgltd_3W8s_{|SMGWhV< zP7tSTudn}d-}mbN554|#rWDmyQKOJj8*;N$?!>Wd)$B(v8YV_dU>?C*`GN6mW?W;_ zkn>MejY_nY4{hd8m#sD{#2}_@ZEX&l1JqE27mSSfDeFN=wj$041+W!Xzy;-Hl}oxH z@Oo|+L7)inItTzV9Yg-3EbT$P?_+W^*ax3dh7&~T*@x~igUQKWV0Mr$yJo8YKj%g z3M7?R=cm)syF)%jzyTKtsQuv89v-dfzEXYc0N6uqq%~?ed<7@~JgfU2 zOL1Xky0Z#yQ%OZq89t~yprlGk=lQ0KkIjFhrKNJ7ynOUdc2XR!nfzvxpB@`VU%AUA z4ipo9S9AVtSVeTbV7iF=mh~y`xe`=wi`+G_c8?t%%;q8}h&zKO&u5xB71=fr+KUZw zn4enr>+Hjj?isn$TCRNDxs_y~hq7Wa$CN@`Xl&qVq!onK zZO)f9JU2p_uUZc0q{O^=J5E?`^rQH(!w)%JR-8s0ZR?a(@g&LMzGqq%P8ybB#{*JQ z4on zGo&bw{^+S%p=P?Ky76}jU%;sN<@nE)@U1&2!wdZ&(4l%*&}V4P)fuXOUiR~UOz!t7 zKdZDT(~q-~T{7y(0OhExTqH}~g^A95 zT!k0KN&4zrSw*aIWajzr#zqkz#%u2yT?5^8+Hd{d!daw3g*#O-*+mhb_17NYrg*Phue?iEd-T4n=0tR>DUC@J&rF?jk#vvA5~jYj5Hjf0R98!L#{ z=Ivvv8>69gXBLg01FDMM3@WEmN|%%!D0nc=nfEd5VXU_0ovAWj!PW~V>5ObV*;nA+ z439s3ePxw@Cb${kj0q=j%I?DLh`$+e5kr!=Wspg@wBVaPAg zO-XPCQ4_ykVwQC?f@b6qViybMJ5YknCh2Q<^WTZ$C;>`aczy;>jO8#y@s$~W0U#Eu z8P_YOFV8kPflzHaKBBR?S4Fvv*!_KTzj|vVh5F|kx|0i4VCKp%q-jK(#d$d@DP>%8 zj37`TehWzJcPM=AsJ94a%%nNG!g7JXj~<61>XFQ z=T78p#QmhJBmc%Q2Go3Al!Ua8kVgVh?;`rzEC|Hjb0bIo)i#?GM;kGFdQpFD`dc8= znk6oqt8sZkIu#d8@1AmM9)Y>(1v2h5v%KoBpb6@;*3<62a`s{r#9}vV(OVjHzD|{d zF*SI>KkDNE9(@kXw@bu_xU&L<^x!l7zQF-$%evL+%4NLG@kxZt+%5J>J^f1|e~9|EuJ z(?q_580-P9w(_L_Ma`KBYeQThNnAyn0F5hT<~F9!m~bbz%%%wM*cl~6B$i%-qI1WAEuSzUas`M8`9|C=mWU^-*L zNiQeRZX2FwQ-gZ63pmvz5lnn$J0v0+c8Q55d7LH0A;Cu&cqUas?)4}zrwb(*NU{G- zFLXlLkm;xxuRg%37$oS^2K8~pRTu;7SH5F66GNHx(n7T+GWVAzz~_3=76eQ-==Hrf zP+v|!v_HdK)qm;mXltrT;$cJ&ujzrhoNqRioaAy_n({xY`F(rdfO@c33w1H~%yhdi zHqu}9C(%A+Yy=AFV*Qt_E_4_7a{P)X%=*A@o%lXWx7JV^p_>=aT!8@e3#9U>=|$gE zAXI_WEOX$ao8P?H{S(E`WG^m$$tii?zn+n!NFz7zLgdfQ))N7)yBkPO1vEIrnmvdf*4I3=XF;LvFAJy3}7ZPi+D4fSl-8@-hrI1N9G8 z0;u!6>+C!pJx-Mi)r8*P<3U|ZS zBC&)9qhH_K|ASEpX2sk2+7DRImdlYx7D8*JT5CMGidv)r4hML7E;jYM z2@%pW%Eb7crz98{4h3qMtn+%_;Eh?uxm6PY|D4X*m3dg z*%5*)g!4-VATEc!7!)|RB^OGLPD{Lh%#~c505Dx+?3GnGGoZF(KU?Q2kpFNES7sE) z#C0Y`59<=yTJwG!S;8gHcNx$A{F+;K4FZbteq_)3e>s7o~w4nq};i6 zAj2vaN@iuzFr^Puw@9$#sA~t*`x1bJkfDw#RXlqAS!u65QvB?OGbXKQ5oI$30G zd+HdpR2a}?OEA2~TOf9;&VVWRpLxCj{8%yE_Z%gmoBjNqAfUqhv+CjuBhAJO%5uZBvRTf=Ju!>^w~c>1K<0#)0pZLL@aa_>g6xN&_P(Bb z7`D~~zW5dJG|{j?PTcy}xC-1|AUM8-I8QAR&`h=?Ion%JszDsBSK=^`>OO%I{BlOI zVeVNDi4|Ua1S74?Fs!9Ru)T{wb^d%ejS~C0@*as9Hk-dN!D$g~wLxbSyetRg*H?pp+JIYU288^_ae-G<$MH@2qaAq7D{@ad!0nvfeSfzKFsW$NR35ajx{}lg`>!rqlmj&;a$-Nq(v9Nm=TS``6RZYMYnsZ3Sv5+ zAUxSA5a&$L?wb*OAc`r{B4I2yy+GU9xqnmPJJ205%0X1_0PIW5zcPyiN)^zi&Vy-b z8_bdBZeoAe7+2GR8}g~w^3%f8Qd0xtL2?^0N0c|4jaBoh`;#+!Rxer@$5*{Fow z=tO|ZFnAuDxo;M5ZZi^fqw}s+`>KW=#6%+%Z79=Q+Baq$tcb&RV{Jx`^a*qBo)T|} z-AS^%n~N&AtKj6WIk#|wnF4JdjoVLM&2~Um3fL>zXu}gVwhF8X{*B`ypOdfnBr51> zxKY!|M{U=qJ#!dB88x+lpv4z4o#Pv=J@ia-t5A;7B=w4*+rdb+LqkEQS_L+fk_%Ez zdugN~M|Z(I2S-)xy$!!Vi7UIH2bDJAc~Gm!xb`x&REXy{+V>5%NFhi%k|;XY@zQX9 zgI0Gm3lIP~)%7y)F&Bv15y-&K(-HKZZFVAsG7HDn)AkNE2Wai469d$*g+;9mlYZ_9w;62MP(6o&abCV@#xYE}sm`U!T@I_q6tM>*_8z}A2pKo5nHZ&x{qrw`RSr1O|VOpJ{H ziy8+tKe`T^gd9@<1>Sv<0iw>mv7uWXTx$A>UJy`|$ZWP`xnB zT@PI%e<=Iiqs7$^FdEjR%M1FAml|}V)vEr=P&zqZB#DOKhSRC&*EeNH=yAu2NZS`Da#!GBW26C>hbXBd5mQ0IDN?riS;24q;rn*jZ^v! z0nn%gDLt+tLKr?2XNCoKva&wwhc(-jQGCUR_zMevJLOtMGC%XzZLSP;{)l#e^9W>X z3iLICA&F2$*=mlV$T7bswa_79fU`8L3wm#D5BOd63-2Z_$1j8HHg%or4ihB!iPy2y zDei=lUVeDUS?i~_n)g|~s?$3p42x~SeQeKnd>m0^AKtD53}Nlb@l2t|WsB|u01eZP z4K*&wO!r6T+s-PfiH4Byaac<(j>XBR2b2zTV&7EPwJb zG0jhWDWKw^UyYSI_5GEJp>kw!j-NQOsJJ2509iayqUnodkU#DIZgQ=`D9INo^Jn7a zwD0I@VDpLJ)aA6ArNKrHO^4Nn0RMW+K+@~!{&fe8!k_HZsX5MY#G|DA>Vs`ppQSl? z2FbAA(z?XI#Nn=AseR<>V)Mz-cpfi2%ge9}Q!(?6n$=MFB3sc(KwPZbS73(`;PK6Y z4w-hOqgQdO0ZDhme=$HLaPxdZIj^J7Xf1J#!==&sOZ_BvZ1P8Y_T*ToQoVGf=aa^q zvENsQbD#2@j;yh#`(BV+ns^zz$6PrnIrh%am&T@hErfV*3UBQBl-u*+o#%AlY<9l) zlbqAf-Rlm&Ney<51DdNTi|XI({-w~?hPpQTecYX{{qrZ+=a@Rjac5`T(cK_p)n_#T zyE#&6&AO@FDv{a<;pm7DJ^;}y2lECNLy_HA@(-Gp7TjyJ$^xqm#j~Y={UOF^fgIJq zjTvpV(GWQ>JhDxnSl`@<^jY_pmfh6d56bg?g8J1=?KR%S>(f!1u@4RL%4!+7vE%?O zhdnj7=mmw=jSh9`?_Kp}syw%(+^}A;;s4QR=__&VsX@8Z)cxyE+l0EyJ>6?przY1) z8jasYB;NRtd3kdEdXU!w8`3o$iSs*B|Ms|R8$Wih`*`?utY(LVCDuw-z@_S+)3M%+ z0xrTVQB9z3byS6X35jK-I`_k}B&q=Rqk;dQeqZp6vFr#bl(i=+zQryn=c zJne@zI31gIy{Xs8tgAjI_2t7pwUkKZUHZyTvR;7(Ex3lekDRCbg<3fypJtuujW^6tsc&Rw)33w4_T;3y-1Wq` zc6V{8d~q#=it(oNIU8V$*u!y{YV9@Os3rkpQWE-rv&f>Fyk0L6mR zJjv9;G>Ev%1}}ZvZG9W*{(5!f_lmircMG|z}~*WR_3TBb_Z zEYQF3UeM~u;7Wj$$GTZVVBkJ|E=f;v0( zDy;#5jmVZ!!zl32D-j3OzJA~Rl=St>Fl^YCD>~uYB@bm4W`#C+msKmr{792;OKa;l z_R>~;HUrM|J5CB9))Yxj3fT>9aO0Y`y{XGK{3IO$Lu*&ZhSsxv?1@6HN~f+lq3gd~ z)%C|odwjR!D#9hyqepO6ZJ+9p>l&p8oBAcOtk%b2&DgATf8Orgl|W=~ZC&=LdMv$X zulHjn<#PYdmX={a*cEUKH8xSK5hJ-OGY_VDyj)1quJX7eOE{Dkz}oLm5$N!M&b3!{wDzp^ zJZTTG-mm8FYrOQ!->(eW7#LoH*=KvGp~-tZWns)ksf_&1OL?c*dK z)L8j+L`nyGZjLUTC0!HSf2&4k3U}OYDB;^I@W66UY;Tr5$>EzOy$$#4c4`S-pIpg* zbEIXF)aMX%ugHF{ThV8}sM}}UKy~74MnK)`B&9}<+A?IkjAH|Fx@u2|Tz>L0-KX1; z$8pC`+Gka>w(;zilJVhsV!6$t$KoLWnsB1?Fi?B#`~}mO=)ZjMlD#GUJZlDofEhOI zt%N5~Vu4c>zz(qXV2u2S5nZX_e~nq~KlrX#h#GQE=SagNjS^03pyUK783P^i4=Vmb z&dS=^n;yRoIC#_he(59ad8ao5PAq9-7~0yu^RLUlVhY;xBb%kJ*WNolT^HltuZR3t z)hyk6*L*sjeS_>X+s-pN*O^gI$dL78(449}75H^jPRNi!#>;jd9agWhAeAXRi|van z^Rv{+OfQQ4CmwDMokI+^5YRpE&VMGl%X1IO=y-RQ*?V3r;(P@h9J1PfMFw_ECj|i7 zrtBoyupb7TdO&d|r9a#n;Zv{EeK5##+H_Tr4Qbx6d}A(FQ)u{~j|zGhvNB!3jYkh5 z28X-ceyH!oAI*LYtZF>EmHW$~MUKrRk03-R&mVuU<4v5kVz>&N0;}dVupUWwi{^;Bh+=%Bt$_xE~fISpZ9LN|GI!2Vpad0t0?&7|DKayYW; zo?9C|A2+D}18^indlzUH04~8}%c|3!sLzxpKWOZ4QJS~Dabq4vCDr_Ba~SA~-X2=* zBrVH?upf38jyJsd40#1I_y{2XDrYtU`bg__k*s3 z7GeP|0ND6J43-iwr)*fK%MaV)H{)yqhre`AOqObr7TcY9f@ZB!CLAqB|Gqd(@6c0Y z1#J)L6$Bb+w^)~*ltIG--F~-6t)QAu7j`Bpj8B_GgKrwGp*@Z9tL4sW_lF+x0UKex zJ36|;7gGVzsz@qlhZA}Fjq$97h{CfTNpV%ik)K|lRBLV*@w+#Xu-qGLh89YVx zSN!P^PF`Pm^I<3#LFNFMZaoyYEWq`m2qBlbw#|nOVp~+OJ%g4?NAFET?hkD}Fev$`3gcMo zf;HPFMBFKhFfF6tpyWrWplVnB5nbT$T%NKPFd5*Cv>!Z9X4!K|=lSm~iSL^I5rR&q zAGBS!Q9$$WSMGDp3S!b@U^#qRSAytB$PU^*IJEBKR|?#i_%=}pN|rv;l5w6ue+LAA zk;~xAGBvw2C2FLwZlv%a@6@+R93aup##8#x4u`9~74IUo5k0A<%% z>TpN)bORI2N3W}&@+M8?HE}^|${hc%!(qD3gg}s z34Ts(ruaq`0Cz?JU;zN!!iQkn061|0uxkQ<@DBip>{A)%Na%uBe z@&bSJ4FAWs%7i4=^Vn-AL}S2rU0&v<&HrVQhl zI$E5e&EvzV#GfS9K?gie{LF+Y- z&nUuU@%7wVr-zL=JyJ4~RzXsC`i7TO$|CmZvjeJ|mT+CPjW--hY85W&)zeoyd^8zH zDrYxUAH_4&obKh{w0|gyY~v8-5}!I{m|0sY`LTPww`yf)es<%STtNbz`>A&98ujyg{GdP}QG7 z#s--z*}|&kR1bP{;o{-@_I$Ec*$217X#rcnv2SjwBs{HY)S{d9jl#-SPtJ zB(Wbu-(L~%&2wICJOD zIA!SQV#VFOD88l`*Bx2~H;O7o{ZXgdgDV-B!SA8(Sg`{4;G@Cp%P5KZ5CV5C)d_OC z&zy`kBsdJ^lao6ch=D=RY*mp5GbHk~$}!9H>Gsr4bBHCj`B3 zc#8`6izOXBIYtMg0tez7(fNvP`^p~wD*t4<@gyioG1idJXY(vhd#o~HZegu#yg{PR zh+@^Y*Nn9w+qk(6@@v;9bAQIZ%tP2C07dOi*hLVJ+vUZNII4=JnlYKMf^+N}infCI zHKJp=&^29LRQJ&9W$!~%_&95cFS}v4E>FTP?^2?(C&Q{iH0cgu&XG;IWpOp>+myY%ikf8ppRlLW z_~J8OrKALwEYcErqsCR=I}XvqX8-DO8;9`Mw>_V6r#LpG|b2GgK37j4C9+ zAN!|d&8sXC{#RkAH!hg9P%{g)hJganQvNcW(Gxdq#@(&rm~=&HcWUguA!~XAOPX0; z{Jw(mm{J_hCH_I#YQATEx-~JDe`v(GacnGY6ZElyBZn;<-rTarE2{m?nUsaITt&x; zQ}coB13BMVsa9@*X7pi*+-ZlFxvPY^r7Io&1WFPfCFG^L17v86xt&>6U= zZ(gRsDW&|8W7jI5`sVBN*gW6fKN3L$0gI?!B4Tq@8+D6m(MdBzO1t*-FPLcIcZ_NF z)zy-&pY?7QCz9#_B*bTFPp;C?;>nNf<3)NxIDG06ymXX(xAx|-fwb3d)aOU1*Vdoe z&+MPK_G<{3Z(_++b71cH3g!ygBs7o`kkr8}^FH}g;6)JNDt>t^)v}U#A!G43MaY(( z%!)dW7n4K+r(>3NekH^FaHtp4L5A^Ah@L8lc2KQEwm}P3pI(3+gL_q&m6|K7zRN~a z46Zy3Z9FAJwC!)>S5To%bFIH6`kI?)d6ua9Hc@HpD(hb5;$ z0Xo>-nY~Mm)?MADj;T+1c#V1F$x$uD^0r)QV|9CIM7Khh9|;7#g(QJ75q(9toIBfS zBRYJm@7$S95n$U;bp}+#q2UoxRhV{~1P@_fBbPbjg_kfzTWNz34gNo(zDD~!f`xPH zUX>Q*tF8zrb4|>S%~x-JU9mzf-~O?oCnq-U7=K*R`4^GKQ=;>9_2LRZmsL*L zFH2@+GB|L9hlVW}-QYY}qMiEGD{5{;{B3!Gu%q@F{_EjO)D~fMr8*bgfQ|(`|1(djCUL!UB6?{oYA|@462xwFD2iiBRr9NCBh9g0-tULveN>1)z8b_+ z6i@M8&#_T7BIBH(g0uCplic>K6o}?)UpWj{QS_k~$zh-W98=MtUQ0^P^o;ZOX0Y01 zlUgQc2$fQq`Wc@U+u=Q2ijt0^M&>)&(Y=Iec(Mez9fGKDmJs10swZzJ5fiX}mZbO{ zg)UAQA*p>4wM9h#(q(TRE$JKGw?#T-|K;s}AKy}Jz7-?erVJl9u@-;&CTF9bj=Xb7 zY%Y!RmQmlmR>886436ID(&TV+JGQa45NXX+J_DXe2HG3QA3G?}@3t!T{l_9Kkuuz7$-6jDOK4OrPHq|ybRRjA1LfvviD?!= z+UY>-K>Jj5;r>KU-_J;mKVjfaT`xqV$KNqVxb{9d$#4I$UH}rFudYSbpxGakkle%~ zvY{k@`uD9{Lu(2?ca8_DQdVtA!RAZ{B7L(&Czs3YArem~bZ1mn`Ijc0SW>`x$?WJP z9m~`U@d8bc5rjuO8HbdA0!PQ9=I@a)&?K6q&D;_gTVGy+bp_;AxE{chkC$CL3W@!*D>7*sP znP=Adc5kCH=PbIJBw)b)Dzv{H+K7tfG*yVVo=X#lmx!VaB_l5G(yr)1A+#pfzs z;`WY|W1Ms{5ZKF|pDJY8;+Ux_pOwz|BuKK`Qis}nu(@+u2BL1w+)?j7W{eeKn~$qx zDPHG#@J(RtvE8zu5^DR0!*kEf@-~X=?nvG`S#ePy)L_H@n`x)x2Q&t zLgsLG`f`htmj5nbw_5XdYWxb))x@!q!ki)16DrA3%av}*k?_%=sWE#| zsZ;)j2yppzTeL1&u;KEfM6Q3rf+;3Uopi+clZ{zT`=V4*Q&ydkW`Xhs8{p2z$zib% z*|db$@%_!ejn?g^J>S+?S){0THQZ2D&`_b&X8zAK9P&4bd*TAFFh-u5U^SF ztM8T9>4c(Ake2A|#ZIL8Aa9p%F$SS+Q9*fO8GhVB*0>xVsEqncFl+l&~YTdL$2m_^0G6F0kpT{LCn4@rem`L$^S z=idu{>$IVJ;zyakvla!odoQGKx2bkD<81ku%-0^>NX=b+cFG11)%=Dk6w{_^9~`1z zI*mtIdE7WB>;xFXK86hhdQ(u8wxOdY2ucsr_Z8nqe~Rg}#w|L)8gEIU)wQ5+HLyhc zw=`jOky>JZ8eteZ;wz*$t%gPT74ktlh-&+hZBY9tp8YmMP0I3B8~VkGL2e6;ZiKli zGu)(Ue=^`1d@qDIOL}AeXw3h1@Fc-%_**1rZb58}_scT?3QKi~a2q5%1Y0vkJ0>i3 zz4}lz^XxV$X9)WO?J**h)MXeynj~agcL!koBV8JWsr#f5FSaH-T?i}$ind9M`6bS^TTj7F zUkjaAj#mV$FS0Phbma#&kfiR6tKkLAVsYd&2B#@WtSao$U!iwS@~5+y7#Rfc#n-*d zHacRNzH-IgCATrjaE4HZE1bSod1HUByxFxgNK9GA+J@Y0{kZm>Zqic~1pPD#Aq zRCM8DY#*l?a0G&$mjpDZh?VoysA0 z!Z^mx*IXD4PU^Lg0O2myD-w>fgbg9xGllYCKFF!lK->Nci2p``l=Mj$L&2NP&>`dZ z<+s1tP@{Pld`ZTkJ3Hn`pbit1A;bM@n+8}A58Pk)s*=c&we*MX#{Mq`X#YD+T~Hta zSd&W@Il?Y8(ijgI8-(-(ZeIYd{vX#{xn4cX;->^%Y7BG>ylXHitk`eJA?GMlLBDeJ zzl^0M0qVWZ+=a~7MxF7NNI)9_nD?`-l=7gJz-EVk!4;juq+*?dkw3xG;pr%K%;Sjv z$y!9HG2zyekgfS9cFfdiC0F}xTpt1W!S^W=jfDDuq>bMw-7Wj>eN3y6H!`T)yh6@0 zMU4p+i)()S{Dy~a@tpGOmrU(E6(&t!vdCJ=uj2k8tbzRi9-&eug`MXVl>kYk9r02( zitP7mWMCq9*5+;_TB481G?uk(Ah$JP{D^}N{IG#WH}h3}{kV4ica}CeiIp7Xg{jet z1q!oPa(7{por0AfNM_wl8D3|Y-nB_+jp2QU5(S4UjVn*#UnqgEPJejm9Y;8EyWndUZVr&omfn4YgNv}IT}ewhi{xp}Rf zg6&(Y;-^s0HZ*cQEQ!Xadk~=j#rNHdR1_{+ptbJ$CY!CR;^1{`sIkg*``?t~Od$Ck zi-$`^y5w9#;4I2)@}%qBL^M`A&Ikb{Z2wm2I`^S6fn+j@#hqrc-9MFu#Jc;ix$_nv zfW48-q{V0M{^r&W!_x$#Yaxo$0iM`^uLET!bP^gsR5el+107Tnd(RKPPL3t#HP6xA z*w!Jyiq-se5S8ED4`93BbpE4mq~XQapR%Fu%_2mA;a~uAHj>HE&7whdSj`)TT5%q_ zmVldBbWmJ(5E=6wg&vD1>T(xmBjl3l~M{>iV@WVFpOxbOXW}5i@b4+?xKW{F#POi0Ux*X1^&qfM?J4I`Kpq3Vljs)@?#u?Z!}mhVr>Z2slQ>2KzfZ>*^LoKq1A5 z&^)kywtK7$zBzYT09|=6nI(TW;c;iu`w!C22(Xsz{$~Iel>i8WLe-i#S4k}h=6SDn zvCXC7B3Ee1xTAGDfwx%!tlmVIV~8OGr+`o{jiP6T=yQ`5$_`^{DP@2{UDi_C&gRWq zJ_GfYg!?O5ENWvqN!glxja!7`6_7WX@js;vNjF{&~gfj<^1g!(iy z_{c!eCN13Y+rb-uzIm@()P8(1tjnt1r|V(t)fA7CW{+W9>;0*h#BWz52pc3NF^oU zGqo{sNiJyk%mI4S-v-c>=l_zN?;OoX2kS`fVL7T#@4By~-5t|!z|8X-l!4PjDBz=+ zD9GOd!4yVNVB3-6o32%I)Bh28@{z6R5siPAtcd(h=2i|ePjg2Ex|M%`G5npChD}|6 z7ukh?S#fcoQSu(R3e*z~PXEiku|!zjn?-HSN67R?fbF6JG}SKyiabHo+mOl^x7hTz zx35rO$G@GAjqHt!*N&30U}9DIRIUe;5V*d8Gcy@j6llSRom|N#{EA+4{3pD6o5)8d3NKmi2yr{;(5RXx=i#`fy}@)6w=;{4Eu4%+cixL z{_p}+f?i4;+Ti>Sa!_I$FbfDcRSCSDy^4iqn z?em!++d*|dc<`f?AX!cO79v@z?8l@pfwoBEh4LN+2Le1$qpK0NA;-IdoW+y>xZpgo zMav^E&ETq(3C9#NjKOy*QREW-$W4$~zP$QK#9vAq7ySU^DQq|-e-FIICdqf~IS`cEBU;>^u3Qr-! zDhj~r z^Q{*!hqN<2+w7W`R{n_q+)CQt$iFYg5TF7<&!X8V+DHLuXe%2UAP5whhQN@`?@DF$ zy0gvyC=24up!J>6!8tAQCKCez*HBdgV{U&f*3lDfK?0?3^))9%5_ zUxpb(2R*fM%g-r#I-t;Q52JbbjFA9qEZ~&M{=*Q_1As3axPUhc1z$XR0~THuGo}`t zG^Q>@lh0~UP6|&?(GRNAisTHUw8u0kE~QqIfaGBD(qdE~c+wToR~Jn0|IA#CHZ>_! z1-5gCu&hUv>tO~*xb~fXCl}&_@3v*cAKgR^3S;T3Zd$$dU&MekOng8I3HF6O6Ys%z zSy9h9(1uTuejo^fV?}SLss%I>)PrKOp9&(l1a2|NOzY$q3tozkgG@X@fkL`GWDO&} zdWg!oeU^|-qPb1TTcXdmizBvQ)s8hiRCfQgjtvFOa4p;;yj3HOy)e}^Q7mW$Ba*gw zr9TPgp4mbq#elU`6q7f*057)kd4Zi(ZW7Dp*%Lg^o1xN#08PDxz|7C6AX4V6w!f+QbhzqemotidG`yD$ue+xlBH+U z)+g<+Mz18y3pJdh;c3XY5SE2=lFD*)<&{D2kvp|my0!QDur}R--~%$EdjujZf28rV z7u};V4G`^xl4sS`*OtPwNS`a%z7|frdw*-blY@%{_|ZYNxZ&-YCr|JF@DSX;GdqiZ zN8(8iQbGIc^I7z>r!hAm$!@Jbj|cQ)F0yQYSDP_nWuxH3Lt4wilYt*H4uoKa6htz- z+IsY++$`}xcwotD%86kkenbX8pLx}O7rJlzNSRrI!W%^L;AL%j>ZE6_QBK;|8528a z(eZm$IBtyqD26@u`{OmwDVN-HkXm@x$qvg|dBYgtXNCo9F7|azeH?3eh#; zp;EauJXg!0ZcwwW#ElNvxKBDpuIPGZ$i6({=!so;Y>(w8;yo0x5IwM{`UCPz?VL?; zj5*8e^{pPAp#LkKF)7`{%B>r<7;y#2tOoYXQge^k4LwV6n|14i87l|L0PQ^4cEY_% zK9u=Voqh9VCOS{1=US&rUq`8LRS~#<|I1xaaj`NtqOubLvN-bFOL?!z@OfqcY%U3- z>ZFROs%s^&>Qnc7NAY9Qx}_)gZ-X}n=8n5}Y{G80#HDEk0(F5DT~E~5 z`jRug;JIb5vY{G0@yG^+iLTg7R9omw)`?Zk@SD9DM;)tTU zf6^N`6=5o|xJ~Pl`CVf_JtA|x%+u;vG*kM8Lz}t>5EcOx7DQBTHOz=#;nZNf8Bq6zF7Z^f}1Fr>Y^eU zDrsPCK8-1vzB(Nj_TDKI0)8UGupM2_1v*(xye)p%@+tBUNgf~C3WHlmcrwBKBV|-% z4^fu;DE~jIcugC%CuR{|4x9|*N{lp|=D?5XBneA0XuTX6_M|`3V7d=dR?C$kYptKB3$pN3BQ=MMXl1Xts5 zDs?XXLgk(~X`}zRGeIOptVc5~lGdkb0dpFrh$J1bMgnQuhduWy~$VNCm`2O7X5roVVN(q4xBsjf4 zeL-wQ%6aAG*IG-%n8TYf#C6zruvq{20Z)t)K1f|Yy%OIKM3~@^e48JdFPxYaa zR*ihLGJ;d``Py)As@l!+#$fMXT1?2Xl)HKPGdNxL8$JmgVhxpl9jf@H2|?Pfv6+l> z@bgrSDZEd`&GGp#igLi7bT8h>jj4e{I$?_jh8)Z7P{p{f(z9gQ!w8Dx-OVXu8GL(H zuH-G>8=J=l(dkh|o@-UF-s@utQyK?QDNvMG!(k7uFjcO#3Z(x#YopnxL_45>LmJ7{ zdGW3Q&{bkLE;Ck8r9>san5~_c7ZUw*@0izq?Cd_=xV9bRJg51r4@0I*=|(8;Yxz z$e?bUl5b~LH)+3ro7HZg%@+5h-tcYU5Knz#1A+-5kEMKRZKd@3WPnsEuJHh5(z%IP zlZXkctGu0H2+h$~J5_Oa`%wGEJ1f)^+gN$QpU+`4QO)y=kHLJ8jXpW+2qLLqZ8(hv zNJCl+s-4d{If)7}us+lRz*MHINNF=sHcRjYAh zMFXMQ4*5BZVv9xo>3jC6nbV>1B5S6EW_^}7VWtC0(Aa~+;XEdxaGamH0x!J6rzrj- zbUjQ@TRgLCGroKdLfwBv9{FUVx}ztt_xmu;rsX}z%-J@>>SvZnK!649Apq5^ z&sa=gTV*k$Igm2Rpy9vha#j$URP+#V)^{G{fVu4lubnDR1=5P8yvCOotKh#w%V9b+gAw>Nt=1I0RP6I-e{_qg*)s5$bnR*w zs~KjsW!P;uDe+$F++qN$S>smZ8bcxnt08!o7gG|Uo#Uv@1lc5vA*$hh(rWyOIOBSuA;OfIF{WKJ{U_1cl zyGAi|8>2~t_W2VMb;sPWVdIX~)%W*T_n#&L4SDbHYSn#y!w38c7g;(bxF5UkE04I~ zsCvA<?9m9<3(aoxF--0%tg^nIIP8#UWhv?D`)z6%hIeO5 z!>`KiHd?l3&0vAE&nsO>5~j}WYi(B|Ur?$JlZZmM1x^%o5WV3B>3?js16AT zbBipOHFBW9F{|HSXCz!OS6{Wt@Y6F!ir!yblDFd8KI*1wgGJ^k#(1qHSJ!nY3#H1g zTLn$mXN^n8ixGF%^V0&aAj=tk^FIW3418H5T3|lr_uu5MLN!DVvLS$IykfcG`sl>) z{_6U!wG9*0N1sBUFK~8#$asjHr5l!Tjq>*~TRD(s3vLXUcDWDv<`R0`L{t%a_$ z6edxAzcJc=A);Gt)7`gfS+W*w8|Z@y-pLxM}2urpkfBM97!pA^U4h5=za`W)79gi6dXuY$U;!2@@YawF- zQ}GV(X?Vsk`u5gh_R}=of-VxK@AD^3u; zc@b%yyw_jl;D{m(2kZ3_JkV%=mL3K#Yafl2YV~hH;l($!L>rl=68ugRBJ($abhD&3 zJk;0R=MyXJzxua!wW;0v|MF=LyUAKw;mml7gbVEPU7iHl;@Zc3i{`t2pX>3aHu3d) zp~tPTD4gLgoZIpykuk41QLs&Wt9Hgc5d_0B#?hpKeP7PpMFa~oT{*^i9+o+LvG1#G zpZPV_G;2OLoTs^Nq~?*36m;K~eUk?SG z_mAicRjguW>U1Y5#$1X28Y1-gA${E>ia3m@CGU{%-Kr(9`?m1lDw!^h+fdNay%NJO z1^;qi2)T_UUGFBr;K@OK;-W@DT>qr+M8fz!Z+QB;`fg7F(vAg$rwd;fFKdrK*QR38 z>cs}TP)P^-LYj5xC$e)!D%Jk%r9TGZEJLs4s)c2Vm(6XciIs|%XDPE3P*!kpjfu<| z&*d$@<8V88#wd}8hPxwjV?d)<9%7V;{Zs;QxZ`fo$zbrVp7UMi=EWMX2CZ`6CKT`e zrm|PRM9uGvnde@*O0*tcSbBd5ntFg_hRI+-k1CAwm$mbNhEY#pG!C!hHX?X8`$*xL zx)B2X97%b86{5m`Cd~vCDa|XR`VZS# ztef{%FXx{;S16Z$Ut12ll#9(f_PT=YDapyqu=Tv3%pvB;zu-Cc0$YtE=H_cipQYcyp`-H^W4S7h-;Pm!4O;Pv6ZL zp^{gO#f%G5f-{X(+jM*-e(Nj==sSB*r>;(U11Kt^t15j)fKg7+vr=95Dcfku^q}>K z6Vz6YQ{{XeZjtHo`uE378?4fGiXNBOl-*Ak?pyAXd!w2WscmJi$3B!^@o1=WtmVEn zrvHErh9$l55Y5E=dgp9(t>wwb{{5saxxm`F!eGOZW~^6FKePhf&{U&hM?Nc%VJ9ns`c>>Hg@d;$YP zL^smhp19~z3sGDJ91k35wkpBh#v{I6D?RS+h6CX0(u z0z=Xg(x%{{rBY#L@y)&*aFAKmf&uC)#pZ5eU6?FJE6k!da}%`w1}2wk(a+PyG>al9 z#TZS+sShGbxm2vw+w@NJul-;3R|DVqM|iAL;mS7X@a3Es8E`~2beDsX^6-B}#fL-2 z>7urrpY3raHH_b)&2(dXZ5!ZoZ$~o6d(7y3W39Z~qE@0}DO6Zo_~>G>y+lH$N*ai< z`LUDd(dvcXPJ@P{djPfo_P@|!k7oDWFJ;{ipgpg(xHT;1OaRwwx#-K|DgP+3e|t2( zJ^7ZD5k-ryCyDR5`I`+tN%a^tMw%Pp=Y4mY|49Z{C)8Au$fkhquZ7XUH!O+Y|Dr=k z!p%2i>EUe-l*)gE3!bn3agpfY>{MGVia|<<-KQk`?*HM=Cy+F8OTdAAi{|U&A?*Rn z-u=1F*T)`T$zC+ul5=W%vm3mM(TEinOeCHUn@IWe!}HnE%>qbCNRZwD>}c<)Bm%O3 z6iM(pB)r3FJ*`i%HC)#HcGTm!KBYU-Z=H|roCRssy{5!CXI?b33s3)EdbSPsxU4lY za;ymsVS&bJoykMXKQ|ffTRsG+=OJGV3d?p8SRsgpf7*#pM<=Lure7*LjKBK$_0%w3 zUodoAC6sIE%Ln>c9GU25VQi(p_U4vY(w2e0xK>~OBTG8=?F|TdF0OI?YiF@@cz`s3 z-U%^wx|>Fig7&8=RT5LPi*s`B8SacI1%=l8byu%uO{W5Ol z8#z9;Jky8>o){0CfiJt0mWXg58pDgK`v{-S=$&>aDZ6f{pa@=7DnGbh8*2m$MmiK= z+%M+u!Bci{MP8(pB%|+^zxM4e124$&Vq%`xt=EHg$ptS%&*OxIA1`A^lg8<>!h3LdOZoV<-WRmGbA)ajnZwFW(Wl z2OSb!?Zf+ZowB{fqt4sOb4v;qH?CLq8uShABG>-4r#pO`D=yC-n3eW)#bPb8Y;2Gx<{dXjBH0#Y z-B3Elg6`Eh zW;-+?cumk{ZrF|9%Vy$vBggsk1s;P!+C93&)d9k=Yd2eXB0Z)YtwC=5;n%3RT24fE z>kHsWknt`3bmHaeUFevZZ*yi{A%g%RA+nW*ly0Hb7DqWUrD5f?<7LDbl%2riJ4a)- z(44pNgi^2m3~OSD`n;R*x=eT-R4*BY8o4G4C%V78^I@i6`TGewcJZ*=)tcAWt%S@m zMyz~BSi-QDzexK+0#Mw}yFWGk-5I$VfkdP_h)AeWF8796S0SHKIn9m9973@U#HN?8 zFH^~8N(^Po?Z_|oNcn!U#P_^gRIgFJmiImon?y!MKT@P0Uec!G^tcp9-&i5Qk+Pr3sVp(8S2qWcdtVMxt0 zU4LQfkGY>2btuxLkltmcx1>n1uL|OD^g1Bly>EBEU5S_IO{@tp)44b;!i6ZOqhl=* zU;o#d^E~9-zt3lk=-wsRy|o9LZnuTax=lNl#=k1|nR(HHVON(H_3z(2_q^WMhRO2? zk{&b?%vgxK3#ORbI39`3d@G_0f-OG|lgc;?#WEEq(J!=#jLozDg zziDS#eeiLP91h!G9Yw)3nc*SZ36T|3mCj+dHGnZwtRMwV+Xm?Dz~#T-$>cAe^T{@J z|Km|&k#0$h{OBUq?wlJ;Yzw_}UEJ%Boz40+_NtgR3%dGbmYjuerZL`JuxA||oCPw| z)0$p)?st4iMLBpFP~>+v5pH5OA<`ppjPWLQPWc=KFBAu@244k1Xzph_+cgr2Sr;~h z?%^#y@P?ZOl8Ez~vdW+$!ua4f?WFNhPLn4^Z zUZj3FGPQRX8Tzs5hd9Tnm*A~*tu&qh*tT+)4!Il zuvLUaZIOq5z9E~vyiW23VWv(2&RN|Qw_kdx%q@kZoF=e(Z=dz|BLA~mQzoWVKvE1onJk?Hebjf+# zh%p>HOFMy;65D~t65Id%ZiJ8ZjF-nwYWmP0sWtYtQp68)V2E~cP}WA}-}$J;1vU@L zV|&J=EECFj)KYW_Pf=R0?^DVz^+%L^*fN#16Hfect7DPfVl*_t^ec2t0!cD6>MbE@ z@cpl((Z9>|jDcyANDJqamX-Kg_(Ma>uHBgue#vcL}=t`OTissdT%SH z9K|J-`vp=_+BwaxlMM06URUkH*Vx45a-k#A>wVoYTpd(GN4N$eB9C_Pmv=TYtZ6h# z^`2zL!nLvNK|6XqJe2l9XBvZa){R750ck;wvLfoZ8+%Fren_=zDXVmRni>CqP+LKd z5VOSZCGoyosD}D_pSvV4QIRa!>lZH1SmaSgNkl>-XOXloc} - - #03D47C - \ No newline at end of file diff --git a/android/app/src/development/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/app/src/development/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index 7353dbd1fd82..000000000000 --- a/android/app/src/development/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/android/app/src/development/res/mipmap-hdpi/ic_launcher_round.png b/android/app/src/development/res/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index 8807b5a298f43fe1a905b3a8c4c8225917e4bcef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4470 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 IM6N<$g1ZBtt^fc4 diff --git a/android/app/src/development/res/mipmap-mdpi/ic_launcher_round.png b/android/app/src/development/res/mipmap-mdpi/ic_launcher_round.png deleted file mode 100644 index 4442d28de683bcc79fa92b2ded264cd17ced3b9e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2851 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 diff --git a/android/app/src/development/res/mipmap-xxhdpi/ic_launcher_round.png b/android/app/src/development/res/mipmap-xxhdpi/ic_launcher_round.png deleted file mode 100644 index a241d41d34fe328a06a70b17b2032a721f3c19ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9945 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 diff --git a/android/app/src/development/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/app/src/development/res/mipmap-xxxhdpi/ic_launcher_round.png deleted file mode 100644 index 996266b64f6ee730afe2375e582d1975a44a1805..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14143 zcmZ{Lby!qi)b5!frInN%T0pv_yF-!A53)cTWiGu13aMG zNU2EyK>a7|2MaXt9A>Ggr3L^#OaK561%P|73BC^iAGrYF&>R4S(*c0gCA(cy6uf}` zRz*P;K>qj2?2w~qmUb@J)XK{s?Gxg>WK3NCz5(7y| zxoDjV-jLs=nJYSkmYGc4_Ns4V$qD&y)-+gTx>vIN1bbGtcU4Zu)sAt)um4%*^+Be` zEGBSJaxX=XytUFjCVYMd{OnjTrI2j zfgo+Ca@VDnWE$3;f|rKno<>U&Di7HWRt3~BtXH3_zR|U_sGFv~l(g2gDSvT-(LQ9N zo5E-stj?Q+G$I^3(o^54YD&IkJz+3yb%i3EnGB}v2)ckt+V?CL?W?s#ET+D37!E;Hgg8{4A^5DvCJL^M;E5y{+zuboomU z=LbNO=Pe}t|j zx&0!TAm=odv(&Kp%#_YrtCycsmXQZ%`n3_T3^AP_f{H|=Nod^-43R%mM5R71^Xim! zeXJXJpV9cd@Jk6*mv`^ftn&CH@FC`~$;Ru=2%(>~l19jOdxdYIiR0&8GP9JD3E8rd zQr%^GwO3u;kdrU`5rV`7;pI5CBeN<`y2sIk5Rd)sM?vxRgCc##OHZc!SLIBR$XdOcC$ZzBG_8 z_FJoZc6(Rj&)Y3%_y-|t>46&@$Q-K`f_6UW|ZaGERI$$V8g z>$g62o)YOSWpAYs+x=gYHUAfZpMoZ4@6l)MPZV%a2tWm!1@2-$ zkMewu&?~pBQ2*B`?dcFs@~Fx-SC7TmK(xaxo6^v)-elCMS0Eyw98=lkJoghXHbrQM zC-34<$~P(5vKm`{%^s7uD;@C}6hlRi656E}oRJ}wM8B_OBc;WqCtD?oEoHE!F3N+x zm&HSRFm^V3-6{wu=)0IOOCn_k=ADW;2JM_1`>w5{rWV3$&dS7GPHCSOf`c_2hO

DPwUE)|?^6t_3O)24|% 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

WqrNLIW;Vhhn*uyy1qvUAW3q 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 - - #03D47C - \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index 7353dbd1fd82..000000000000 --- a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index 94269b994fe4ab002fb3f8ad13a21457075d7ca0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4142 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 diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png deleted file mode 100644 index f533b4e8d2300abcb84799d6f6516e7323a33b1a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9387 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 diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png deleted file mode 100644 index ee08d91ae4a78072c3c4029d2175ed2f1bdc847f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13361 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 ZgRTGnzk$C=12fnFP*c)YtdX|}`yY}mU&H_a From 5bc5f76310e543fcb92a0ff08b6569e5897ade3c Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Wed, 20 Dec 2023 10:57:44 +0000 Subject: [PATCH 68/98] use original adaptive icons for dev and adhoc varients --- android/app/src/adhoc/res/mipmap-anydpi-v26/ic_launcher.xml | 1 - .../app/src/development/res/mipmap-anydpi-v26/ic_launcher.xml | 1 - 2 files changed, 2 deletions(-) diff --git a/android/app/src/adhoc/res/mipmap-anydpi-v26/ic_launcher.xml b/android/app/src/adhoc/res/mipmap-anydpi-v26/ic_launcher.xml index 1084c240820a..7353dbd1fd82 100644 --- a/android/app/src/adhoc/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/android/app/src/adhoc/res/mipmap-anydpi-v26/ic_launcher.xml @@ -2,5 +2,4 @@ - \ No newline at end of file diff --git a/android/app/src/development/res/mipmap-anydpi-v26/ic_launcher.xml b/android/app/src/development/res/mipmap-anydpi-v26/ic_launcher.xml index 1084c240820a..7353dbd1fd82 100644 --- a/android/app/src/development/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/android/app/src/development/res/mipmap-anydpi-v26/ic_launcher.xml @@ -2,5 +2,4 @@ - \ No newline at end of file From 7130c0addcd184831f6274ee3806d09766698609 Mon Sep 17 00:00:00 2001 From: Luthfi Date: Wed, 20 Dec 2023 18:30:21 +0700 Subject: [PATCH 69/98] Update .github/PULL_REQUEST_TEMPLATE.md Co-authored-by: 0xmiroslav <97473779+0xmiroslav@users.noreply.github.com> --- .github/PULL_REQUEST_TEMPLATE.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index e516b8b3e0e1..19b62f0f93d8 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -115,8 +115,8 @@ This is a checklist for PR authors. Please make sure to complete all tasks and c - [ ] If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected. - [ ] If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account. - [ ] If the PR modifies the form input styles: - - [ ] All the inputs inside a form should be aligned with each other - - [ ] Add the Design label so the Design team can review your changes + - [ ] I verified that all the inputs inside a form are aligned with each other. + - [ ] I added `Design` label so the design team can review the changes. - [ ] If a new page is added, I verified it's using the `ScrollView` component to make it scrollable when more elements are added to the page. - [ ] If the `main` branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the `Test` steps. - [ ] I have checked off every checkbox in the PR author checklist, including those that don't apply to this PR. From 5376ab570b40b4d27b53075ebb1626e88aaa5b76 Mon Sep 17 00:00:00 2001 From: Luthfi Date: Wed, 20 Dec 2023 18:32:28 +0700 Subject: [PATCH 70/98] update the reviewer checklist --- contributingGuides/REVIEWER_CHECKLIST.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contributingGuides/REVIEWER_CHECKLIST.md b/contributingGuides/REVIEWER_CHECKLIST.md index 51908d696c1d..146c170a15cf 100644 --- a/contributingGuides/REVIEWER_CHECKLIST.md +++ b/contributingGuides/REVIEWER_CHECKLIST.md @@ -52,8 +52,8 @@ - [ ] If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected. - [ ] If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account. - [ ] If the PR modifies the form input styles: - - [ ] All the inputs inside a form should be aligned with each other - - [ ] Add the Design label so the Design team can review your changes + - [ ] I verified that all the inputs inside a form are aligned with each other. + - [ ] I added `Design` label so the design team can review the changes. - [ ] If a new page is added, I verified it's using the `ScrollView` component to make it scrollable when more elements are added to the page. - [ ] If the `main` branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the `Test` steps. - [ ] I have checked off every checkbox in the PR reviewer checklist, including those that don't apply to this PR. From b9bf599a7dc51414b9e4d2f346ce5100f6053d97 Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Wed, 20 Dec 2023 13:10:32 +0100 Subject: [PATCH 71/98] fix import --- src/styles/utils/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index 00992043ee2d..7a5df1657c99 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -1,4 +1,4 @@ -import {Animated, DimensionValue, PressableStateCallbackType, StyleProp, StyleSheet, TextStyle, ViewStyle} from 'react-native'; +import {Animated, DimensionValue, ImageStyle, PressableStateCallbackType, StyleProp, StyleSheet, TextStyle, ViewStyle} from 'react-native'; import {EdgeInsets} from 'react-native-safe-area-context'; import {ValueOf} from 'type-fest'; import * as Browser from '@libs/Browser'; From e4048d63aebfb933e6a74a960b53d3b68a46ecdf Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Wed, 20 Dec 2023 14:50:15 +0100 Subject: [PATCH 72/98] Remove borderRadius from LHP --- src/styles/index.ts | 20 ++++++++++---------- src/styles/variables.ts | 2 -- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/styles/index.ts b/src/styles/index.ts index aececf93beb9..fcc78779136d 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -122,6 +122,13 @@ const headlineFont = { fontWeight: '500', } satisfies TextStyle; +const modalNavigatorContainer = (isSmallScreenWidth: boolean) => + ({ + position: 'absolute', + width: isSmallScreenWidth ? '100%' : variables.sideBarWidth, + height: '100%', + } satisfies ViewStyle); + const webViewStyles = (theme: ThemeColors) => ({ // As of react-native-render-html v6, don't declare distinct styles for @@ -1406,21 +1413,14 @@ const styles = (theme: ThemeColors) => LHPNavigatorContainer: (isSmallScreenWidth: boolean) => ({ - width: isSmallScreenWidth ? '100%' : variables.sideBarWidth, - position: 'absolute', + ...modalNavigatorContainer(isSmallScreenWidth), left: 0, - height: '100%', - borderTopRightRadius: isSmallScreenWidth ? 0 : variables.lhpBorderRadius, - borderBottomRightRadius: isSmallScreenWidth ? 0 : variables.lhpBorderRadius, - overflow: 'hidden', } satisfies ViewStyle), RHPNavigatorContainer: (isSmallScreenWidth: boolean) => ({ - width: isSmallScreenWidth ? '100%' : variables.sideBarWidth, - position: 'absolute', + ...modalNavigatorContainer(isSmallScreenWidth), right: 0, - height: '100%', } satisfies ViewStyle), onlyEmojisText: { @@ -1641,7 +1641,7 @@ const styles = (theme: ThemeColors) => ...positioning.pFixed, // We need to stretch the overlay to cover the sidebar and the translate animation distance. // The overlay must also cover borderRadius of the LHP component - left: isModalOnTheLeft ? -variables.lhpBorderRadius : -2 * variables.sideBarWidth, + left: isModalOnTheLeft ? 0 : -2 * variables.sideBarWidth, top: 0, bottom: 0, right: isModalOnTheLeft ? -2 * variables.sideBarWidth : 0, diff --git a/src/styles/variables.ts b/src/styles/variables.ts index 4904a224327a..3bf83545bc4a 100644 --- a/src/styles/variables.ts +++ b/src/styles/variables.ts @@ -193,6 +193,4 @@ export default { cardPreviewHeight: 148, cardPreviewWidth: 235, cardNameWidth: 156, - - lhpBorderRadius: 24, } as const; From 6b1391624da2c59323eb6301e7e05a989208318c Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 20 Dec 2023 21:52:38 +0700 Subject: [PATCH 73/98] enable reply in thread for iou whisper action --- src/pages/home/report/ContextMenu/ContextMenuActions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.js b/src/pages/home/report/ContextMenu/ContextMenuActions.js index 3682ef602a2b..1f82e71ef5f1 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.js +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.js @@ -148,7 +148,7 @@ export default [ const isTaskAction = ReportActionsUtils.isTaskAction(reportAction); const isWhisperAction = ReportActionsUtils.isWhisperAction(reportAction); return ( - !isWhisperAction && + (!isWhisperAction || reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.IOU || isReportPreviewAction) && (isCommentAction || isReportPreviewAction || isIOUAction || isModifiedExpenseAction || isTaskAction) && !ReportUtils.isThreadFirstChat(reportAction, reportID) ); From 102154717acb6e3e7b9bba0c8e254c01c2da259c Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 20 Dec 2023 22:52:03 +0700 Subject: [PATCH 74/98] use isIOUAction variable --- src/pages/home/report/ContextMenu/ContextMenuActions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.js b/src/pages/home/report/ContextMenu/ContextMenuActions.js index 1f82e71ef5f1..2e51ef714562 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.js +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.js @@ -148,7 +148,7 @@ export default [ const isTaskAction = ReportActionsUtils.isTaskAction(reportAction); const isWhisperAction = ReportActionsUtils.isWhisperAction(reportAction); return ( - (!isWhisperAction || reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.IOU || isReportPreviewAction) && + (!isWhisperAction || isIOUAction || isReportPreviewAction) && (isCommentAction || isReportPreviewAction || isIOUAction || isModifiedExpenseAction || isTaskAction) && !ReportUtils.isThreadFirstChat(reportAction, reportID) ); From 2bb4d0c63d233adfc166a19c450c95da04e40852 Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Wed, 20 Dec 2023 21:53:04 +0530 Subject: [PATCH 75/98] fix: Settings - Focus shifts to first digit on shortcut & back. Signed-off-by: Krishna Gupta --- .../Profile/Contacts/ValidateCodeForm/BaseValidateCodeForm.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/settings/Profile/Contacts/ValidateCodeForm/BaseValidateCodeForm.js b/src/pages/settings/Profile/Contacts/ValidateCodeForm/BaseValidateCodeForm.js index d15405f8029c..453da6eb4c40 100644 --- a/src/pages/settings/Profile/Contacts/ValidateCodeForm/BaseValidateCodeForm.js +++ b/src/pages/settings/Profile/Contacts/ValidateCodeForm/BaseValidateCodeForm.js @@ -109,7 +109,7 @@ function BaseValidateCodeForm(props) { if (focusTimeoutRef.current) { clearTimeout(focusTimeoutRef.current); } - focusTimeoutRef.current = setTimeout(inputValidateCodeRef.current.focus, CONST.ANIMATED_TRANSITION); + focusTimeoutRef.current = setTimeout(inputValidateCodeRef.current.focusLastSelected, CONST.ANIMATED_TRANSITION); return () => { if (!focusTimeoutRef.current) { return; From 808a1e11fe9fc08a9ee921fabb7975750680cd89 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Wed, 20 Dec 2023 17:29:38 +0100 Subject: [PATCH 76/98] Add optional chaining to topRouteName in linkTo --- src/libs/Navigation/linkTo.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/linkTo.ts b/src/libs/Navigation/linkTo.ts index 6468670fce99..86558765a6e6 100644 --- a/src/libs/Navigation/linkTo.ts +++ b/src/libs/Navigation/linkTo.ts @@ -80,7 +80,7 @@ export default function linkTo(navigation: NavigationContainerRef Date: Wed, 20 Dec 2023 18:23:47 +0000 Subject: [PATCH 77/98] Update version to 1.4.14-3 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index b7ed29f176dc..91b29f6e982d 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -91,8 +91,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001041402 - versionName "1.4.14-2" + versionCode 1001041403 + versionName "1.4.14-3" } flavorDimensions "default" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 67c47dbb30cf..d714e191df2a 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.14.2 + 1.4.14.3 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index d13112319dd6..3fe19193d6b5 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.4.14.2 + 1.4.14.3 diff --git a/package-lock.json b/package-lock.json index 1c2ae325575d..d69d460c9184 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.14-2", + "version": "1.4.14-3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.14-2", + "version": "1.4.14-3", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 7281ab12cfa7..21c10be9d800 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.14-2", + "version": "1.4.14-3", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From 38974d0741e699ab7088da98b3d555e67e9bf725 Mon Sep 17 00:00:00 2001 From: Christina Dobrzynski <51066321+Christinadobrzyn@users.noreply.github.com> Date: Wed, 20 Dec 2023 14:25:59 -0700 Subject: [PATCH 78/98] Update Admin-Card-Settings-and-Features.md Related to https://github.com/Expensify/Expensify/issues/342533 - we're updating the articles based on Bancorp needs --- .../expensify-card/Admin-Card-Settings-and-Features.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/articles/expensify-classic/expensify-card/Admin-Card-Settings-and-Features.md b/docs/articles/expensify-classic/expensify-card/Admin-Card-Settings-and-Features.md index 3e2eb2deec46..1bfa5590efbc 100644 --- a/docs/articles/expensify-classic/expensify-card/Admin-Card-Settings-and-Features.md +++ b/docs/articles/expensify-classic/expensify-card/Admin-Card-Settings-and-Features.md @@ -5,7 +5,7 @@ description: An in-depth look into the Expensify Card program's admin controls a # Overview -The Expensify Card offers a range of settings and functionality to customize how admins manage expenses and card usage in Expensify. To start, we'll lay out the best way to make these options work for you. +The Expensify Visa® Commercial Card offers a range of settings and functionality to customize how admins manage expenses and card usage in Expensify. To start, we'll lay out the best way to make these options work for you. Set Smart Limits to control card spend. Smart Limits are spend limits that can be set for individual cards or specific groups. Once a given Smart Limit is reached, the card is temporarily disabled until expenses are approved. @@ -150,7 +150,7 @@ Here are some reasons an Expensify Card transaction might be declined: # FAQ ## What happens when I reject an Expensify Card expense? -Rejecting an Expensify Card expense from an Expensify report will simply allow it to be reported on a different report. You cannot undo a credit card charge. +Rejecting an Expensify Card expense from an Expensify report will simply allow it to be reported on a different report. If an Expensify Card expense needs to be rejected, you can reject the report or the specific expense so it can be added to a different report. The rejected expense will become Unreported and return to the submitter's Expenses page. From 16aab1da8797904c55b636a49a0763e81ed1d7e0 Mon Sep 17 00:00:00 2001 From: Christina Dobrzynski <51066321+Christinadobrzyn@users.noreply.github.com> Date: Wed, 20 Dec 2023 14:47:19 -0700 Subject: [PATCH 79/98] Update Statements.md Related GH https://github.com/Expensify/Expensify/issues/342533#issuecomment-1863656681 Updating docs for Bancorp --- docs/articles/expensify-classic/expensify-card/Statements.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/articles/expensify-classic/expensify-card/Statements.md b/docs/articles/expensify-classic/expensify-card/Statements.md index 5b583370b810..894dfa3d8b9a 100644 --- a/docs/articles/expensify-classic/expensify-card/Statements.md +++ b/docs/articles/expensify-classic/expensify-card/Statements.md @@ -6,7 +6,7 @@ description: Learn how the Expensify Card statement and settlements work! # Overview Expensify offers several settlement types and a statement that provides a detailed view of transactions and settlements. We discuss specifics on both below. -# How to use Expensify Card Statement and Settlements +# How to use Expensify Visa® Commercial Card Statement and Settlements ## Using the statement If your domain uses the Expensify Card and you have a validated Business Bank Account, access the Expensify Card statement at Settings > Domains > Company Cards > Reconciliation Tab > Settlements. From 41ba4768680bd8c5fd633c5f39772b7d0db87f2f Mon Sep 17 00:00:00 2001 From: mkhutornyi Date: Wed, 20 Dec 2023 16:49:11 -0500 Subject: [PATCH 80/98] fix crash when trying to scroll pdf preview --- .../AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.js | 2 +- .../AttachmentView/AttachmentViewPdf/index.android.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.js b/src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.js index 40887ddee697..de14f848c37e 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.js +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.js @@ -30,7 +30,7 @@ function BaseAttachmentViewPdf({ onScaleChangedProp(scale); // When a pdf is shown in a carousel, we want to disable the pager scroll when the pdf is zoomed in - if (isUsedInCarousel) { + if (isUsedInCarousel && attachmentCarouselPagerContext) { const shouldPagerScroll = scale === 1; attachmentCarouselPagerContext.onPinchGestureChange(!shouldPagerScroll); diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.android.js b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.android.js index 308d3cf2c0ba..6d510d234512 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.android.js +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.android.js @@ -18,7 +18,7 @@ function AttachmentViewPdf(props) { const Pan = Gesture.Pan() .manualActivation(true) .onTouchesMove((evt) => { - if (offsetX.value !== 0 && offsetY.value !== 0) { + if (offsetX.value !== 0 && offsetY.value !== 0 && attachmentCarouselPagerContext) { // if the value of X is greater than Y and the pdf is not zoomed in, // enable the pager scroll so that the user // can swipe to the next attachment otherwise disable it. From 4b9889fccca2f9721f48c2f80908d48172984d56 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Wed, 20 Dec 2023 21:52:20 +0000 Subject: [PATCH 81/98] Update version to 1.4.14-4 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 91b29f6e982d..ded407d0211f 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -91,8 +91,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001041403 - versionName "1.4.14-3" + versionCode 1001041404 + versionName "1.4.14-4" } flavorDimensions "default" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index d714e191df2a..e825d2a3bb9f 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.14.3 + 1.4.14.4 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 3fe19193d6b5..f3bb098a344b 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.4.14.3 + 1.4.14.4 diff --git a/package-lock.json b/package-lock.json index d69d460c9184..e22c97183bd7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.14-3", + "version": "1.4.14-4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.14-3", + "version": "1.4.14-4", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 21c10be9d800..5b98b778ca40 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.14-3", + "version": "1.4.14-4", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From c728f53cefd9bc8dfaa4f7b178d611a14a55d484 Mon Sep 17 00:00:00 2001 From: Christina Dobrzynski <51066321+Christinadobrzyn@users.noreply.github.com> Date: Wed, 20 Dec 2023 15:03:40 -0700 Subject: [PATCH 82/98] Update Expensify-Card-Perks.md Related to https://github.com/Expensify/Expensify/issues/342533#issuecomment-1863660889 Bancorp asking for these updates --- .../expensify-card/Expensify-Card-Perks.md | 26 +++---------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/docs/articles/expensify-classic/expensify-card/Expensify-Card-Perks.md b/docs/articles/expensify-classic/expensify-card/Expensify-Card-Perks.md index 8bcc11fbf167..868ade604451 100644 --- a/docs/articles/expensify-classic/expensify-card/Expensify-Card-Perks.md +++ b/docs/articles/expensify-classic/expensify-card/Expensify-Card-Perks.md @@ -5,30 +5,10 @@ description: Get the most out of your Expensify Card with exclusive perks! # Overview -The Expensify Card is packed with perks, both native to our Card program and through exclusive discounts with partnering solutions. The Expensify Card’s primary perks include: -- Swipe to Win, where every swipe has a chance to win fun personalized gifts for you and your closest friends and family members -- Unbeatable cash back incentive with each swipe -Below, we’ll cover all of our exclusive offers in more detail and how to claim discounts with our partners. - -# Expensify Card Perks - -## Swipe to Win -Swipe to Win is a new [Expensify Card](https://use.expensify.com/company-credit-card) perk that gives cardholders the chance to send a gift to a friend, family member, or essential worker on the frontlines! - -Winners can choose to _Send a Smile_ or _Send a Laugh_. To start, we’re offering one gift per option: +The Expensify Visa® Commercial Card is packed with perks, both native to our Card program and through exclusive discounts with partnering solutions. The Expensify Card’s primary perks include: +- Unbeatable cash back incentive with each USD purchase -- **Send A Smile:** Champagne by Expensify -- **Send a Laugh:** Jenga Set - -**How to Participate** -It’s easy! Once you have an Expensify Card, you just need to start using it. With each swipe, you're automatically entered to win and have a 1 in 250 chance of getting a prize! - -**How will I know if I’ve won?** -Winners will be notified immediately via the Expensify app, and receive additional instructions on how to choose and send their desired gift. - -If you don't have Expensify notifications turned on yet, here are some helpful guides: -- [Apple Notification Preferences](https://support.apple.com/en-us/HT201925) -- [Android Notification Preferences](https://community.expensify.com/home/leaving?allowTrusted=1&target=https%3A%2F%2Fsupport.google.com%2Fandroid%2Fanswer%2F9079661%3Fhl%3Den) +Below, we’ll cover all of our exclusive offers in more detail and how to claim discounts with our partners. # Partner Specific Perks From a9624b3cae48742668927a86840f41d49290b8e2 Mon Sep 17 00:00:00 2001 From: Christina Dobrzynski <51066321+Christinadobrzyn@users.noreply.github.com> Date: Wed, 20 Dec 2023 15:05:33 -0700 Subject: [PATCH 83/98] Update Set-Up-the-Card-for-Your-Company.md Related to https://github.com/Expensify/Expensify/issues/342533#issuecomment-1863661336 Bancorp is asking us to make these updates --- .../expensify-card/Set-Up-the-Card-for-Your-Company.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/articles/expensify-classic/expensify-card/Set-Up-the-Card-for-Your-Company.md b/docs/articles/expensify-classic/expensify-card/Set-Up-the-Card-for-Your-Company.md index 8f87b36ef3d9..e0ef1f3f00fe 100644 --- a/docs/articles/expensify-classic/expensify-card/Set-Up-the-Card-for-Your-Company.md +++ b/docs/articles/expensify-classic/expensify-card/Set-Up-the-Card-for-Your-Company.md @@ -4,7 +4,7 @@ description: Details on setting up the Expensify Card for your company as an adm --- # Overview -If you’re an admin interested in rolling out the Expensify Card for your organization, you’re in the right place. This article will cover how to qualify and apply for the Expensify Card program and begin issuing cards to your employees. +If you’re an admin interested in rolling out the Expensify Visa® Commercial Card for your organization, you’re in the right place. This article will cover how to qualify and apply for the Expensify Card program and begin issuing cards to your employees. # How to qualify for the Expensify Card program From f4ae14452cdb83c65ceb9195f7a93ba0ccab3153 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Wed, 20 Dec 2023 17:30:59 -0500 Subject: [PATCH 84/98] disable clearReportNotifications on android --- .../PushNotification/index.native.ts | 36 ---------------- .../Notification/PushNotification/index.ts | 1 - .../Notification/PushNotification/types.ts | 2 - .../clearReportNotifications/index.android.ts | 9 ++++ .../clearReportNotifications/index.ios.ts | 41 +++++++++++++++++++ .../clearReportNotifications/index.native.ts | 5 --- 6 files changed, 50 insertions(+), 44 deletions(-) create mode 100644 src/libs/Notification/clearReportNotifications/index.android.ts create mode 100644 src/libs/Notification/clearReportNotifications/index.ios.ts delete mode 100644 src/libs/Notification/clearReportNotifications/index.native.ts diff --git a/src/libs/Notification/PushNotification/index.native.ts b/src/libs/Notification/PushNotification/index.native.ts index d48d56f71993..7b2571eea368 100644 --- a/src/libs/Notification/PushNotification/index.native.ts +++ b/src/libs/Notification/PushNotification/index.native.ts @@ -2,7 +2,6 @@ import Airship, {EventType, PushPayload} from '@ua/react-native-airship'; import Onyx from 'react-native-onyx'; import Log from '@libs/Log'; import * as PushNotificationActions from '@userActions/PushNotification'; -import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ForegroundNotifications from './ForegroundNotifications'; import NotificationType, {NotificationData} from './NotificationType'; @@ -190,40 +189,6 @@ const clearNotifications: ClearNotifications = () => { Airship.push.clearNotifications(); }; -const parseNotificationAndReportIDs = (pushPayload: PushPayload) => { - let payload = pushPayload.extras.payload; - if (typeof payload === 'string') { - payload = JSON.parse(payload); - } - const data = payload as NotificationData; - return { - notificationID: pushPayload.notificationId, - reportID: String(data.reportID), - }; -}; - -const clearReportNotifications = (reportID: string) => { - Log.info('[PushNotification] clearing report notifications', false, {reportID}); - - Airship.push - .getActiveNotifications() - .then((pushPayloads) => { - const reportNotificationIDs = pushPayloads.reduce((notificationIDs, pushPayload) => { - const notification = parseNotificationAndReportIDs(pushPayload); - if (notification.notificationID && notification.reportID === reportID) { - notificationIDs.push(notification.notificationID); - } - return notificationIDs; - }, []); - - Log.info(`[PushNotification] found ${reportNotificationIDs.length} notifications to clear`, false, {reportID}); - reportNotificationIDs.forEach((notificationID) => Airship.push.clearNotification(notificationID)); - }) - .catch((error) => { - Log.alert(`${CONST.ERROR.ENSURE_BUGBOT} [PushNotification] BrowserNotifications.clearReportNotifications threw an error. This should never happen.`, {reportID, error}); - }); -}; - const PushNotification: PushNotificationType = { init, register, @@ -232,7 +197,6 @@ const PushNotification: PushNotificationType = { onSelected, TYPE: NotificationType, clearNotifications, - clearReportNotifications, }; export default PushNotification; diff --git a/src/libs/Notification/PushNotification/index.ts b/src/libs/Notification/PushNotification/index.ts index 925574b8976f..1e5499d1fe7d 100644 --- a/src/libs/Notification/PushNotification/index.ts +++ b/src/libs/Notification/PushNotification/index.ts @@ -10,7 +10,6 @@ const PushNotification: PushNotificationType = { onSelected: () => {}, TYPE: NotificationType, clearNotifications: () => {}, - clearReportNotifications: () => {}, }; export default PushNotification; diff --git a/src/libs/Notification/PushNotification/types.ts b/src/libs/Notification/PushNotification/types.ts index e39f21e0cb92..f72ee1af887a 100644 --- a/src/libs/Notification/PushNotification/types.ts +++ b/src/libs/Notification/PushNotification/types.ts @@ -1,5 +1,4 @@ import {ValueOf} from 'type-fest'; -import ClearReportNotifications from '@libs/Notification/clearReportNotifications/types'; import NotificationType, {NotificationDataMap} from './NotificationType'; type Init = () => void; @@ -17,7 +16,6 @@ type PushNotification = { onSelected: OnSelected; TYPE: typeof NotificationType; clearNotifications: ClearNotifications; - clearReportNotifications: ClearReportNotifications; }; export default PushNotification; diff --git a/src/libs/Notification/clearReportNotifications/index.android.ts b/src/libs/Notification/clearReportNotifications/index.android.ts new file mode 100644 index 000000000000..8f0e8cc2ea69 --- /dev/null +++ b/src/libs/Notification/clearReportNotifications/index.android.ts @@ -0,0 +1,9 @@ +import ClearReportNotifications from './types'; + +/** + * This is a temporary fix for issues with our Notification Cache not being cleared in Android. + * More info here: https://github.com/Expensify/App/issues/33367#issuecomment-1865196381 + */ +const clearReportNotifications: ClearReportNotifications = () => {}; + +export default clearReportNotifications; diff --git a/src/libs/Notification/clearReportNotifications/index.ios.ts b/src/libs/Notification/clearReportNotifications/index.ios.ts new file mode 100644 index 000000000000..74b2c7faa50f --- /dev/null +++ b/src/libs/Notification/clearReportNotifications/index.ios.ts @@ -0,0 +1,41 @@ +import Airship, {PushPayload} from '@ua/react-native-airship'; +import Log from '@libs/Log'; +import {NotificationData} from '@libs/Notification/PushNotification/NotificationType'; +import CONST from '@src/CONST'; +import ClearReportNotifications from './types'; + +const parseNotificationAndReportIDs = (pushPayload: PushPayload) => { + let payload = pushPayload.extras.payload; + if (typeof payload === 'string') { + payload = JSON.parse(payload); + } + const data = payload as NotificationData; + return { + notificationID: pushPayload.notificationId, + reportID: String(data.reportID), + }; +}; + +const clearReportNotifications: ClearReportNotifications = (reportID: string) => { + Log.info('[PushNotification] clearing report notifications', false, {reportID}); + + Airship.push + .getActiveNotifications() + .then((pushPayloads) => { + const reportNotificationIDs = pushPayloads.reduce((notificationIDs, pushPayload) => { + const notification = parseNotificationAndReportIDs(pushPayload); + if (notification.notificationID && notification.reportID === reportID) { + notificationIDs.push(notification.notificationID); + } + return notificationIDs; + }, []); + + Log.info(`[PushNotification] found ${reportNotificationIDs.length} notifications to clear`, false, {reportID}); + reportNotificationIDs.forEach((notificationID) => Airship.push.clearNotification(notificationID)); + }) + .catch((error) => { + Log.alert(`${CONST.ERROR.ENSURE_BUGBOT} [PushNotification] BrowserNotifications.clearReportNotifications threw an error. This should never happen.`, {reportID, error}); + }); +}; + +export default clearReportNotifications; diff --git a/src/libs/Notification/clearReportNotifications/index.native.ts b/src/libs/Notification/clearReportNotifications/index.native.ts deleted file mode 100644 index 5082fe492564..000000000000 --- a/src/libs/Notification/clearReportNotifications/index.native.ts +++ /dev/null @@ -1,5 +0,0 @@ -import PushNotification from '@libs/Notification/PushNotification'; -import ClearReportNotifications from './types'; - -const clearReportNotifications: ClearReportNotifications = PushNotification.clearReportNotifications; -export default clearReportNotifications; From 4dcad9c24cff947bf5ca937a256c298c06e8a3e5 Mon Sep 17 00:00:00 2001 From: Christina Dobrzynski <51066321+Christinadobrzyn@users.noreply.github.com> Date: Wed, 20 Dec 2023 18:18:57 -0700 Subject: [PATCH 85/98] Update Request-the-Card.md related to https://github.com/Expensify/Expensify/issues/342533#issuecomment-1863655279 Bancorp requested changes --- .../expensify-classic/expensify-card/Request-the-Card.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/articles/expensify-classic/expensify-card/Request-the-Card.md b/docs/articles/expensify-classic/expensify-card/Request-the-Card.md index 4830c0fffbcd..ca0e7b4709b2 100644 --- a/docs/articles/expensify-classic/expensify-card/Request-the-Card.md +++ b/docs/articles/expensify-classic/expensify-card/Request-the-Card.md @@ -4,7 +4,7 @@ description: Details on requesting the Expensify Card as an employee --- # Overview -Once your organization is approved for the Expensify Card, you can request a card! +Once your organization is approved for the Expensify Visa® Commercial Card, you can request a card! This article covers how to request, activate, and replace your physical and virtual Expensify Cards. From 6797636de63a0d8c0aeb022d36f5db58ffc42057 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Thu, 21 Dec 2023 01:46:32 +0000 Subject: [PATCH 86/98] Update version to 1.4.14-5 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index ded407d0211f..922b047098d0 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -91,8 +91,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001041404 - versionName "1.4.14-4" + versionCode 1001041405 + versionName "1.4.14-5" } flavorDimensions "default" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index e825d2a3bb9f..303d2f662fc9 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.14.4 + 1.4.14.5 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index f3bb098a344b..47f6e27cef9a 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.4.14.4 + 1.4.14.5 diff --git a/package-lock.json b/package-lock.json index e22c97183bd7..cca83c250cc5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.14-4", + "version": "1.4.14-5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.14-4", + "version": "1.4.14-5", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 5b98b778ca40..c4808f69e0a6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.14-4", + "version": "1.4.14-5", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From 58ae7fe8fabde71059d0599a48a35bff062f246c Mon Sep 17 00:00:00 2001 From: OSBotify Date: Thu, 21 Dec 2023 03:09:51 +0000 Subject: [PATCH 87/98] Update version to 1.4.14-6 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 922b047098d0..981800af5b9f 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -91,8 +91,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001041405 - versionName "1.4.14-5" + versionCode 1001041406 + versionName "1.4.14-6" } flavorDimensions "default" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 303d2f662fc9..330868b262c4 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.14.5 + 1.4.14.6 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 47f6e27cef9a..8af46472572d 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.4.14.5 + 1.4.14.6 diff --git a/package-lock.json b/package-lock.json index cca83c250cc5..39cfdfa82854 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.14-5", + "version": "1.4.14-6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.14-5", + "version": "1.4.14-6", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index c4808f69e0a6..3e63b96af229 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.14-5", + "version": "1.4.14-6", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From ce0500f2dde3976a22ffc8a09e5646e80ffa27c0 Mon Sep 17 00:00:00 2001 From: DylanDylann Date: Thu, 21 Dec 2023 11:43:03 +0700 Subject: [PATCH 88/98] fix add comment keyForList --- src/components/OptionRow.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/OptionRow.js b/src/components/OptionRow.js index 0c1e5161f496..4bc4285a693e 100644 --- a/src/components/OptionRow.js +++ b/src/components/OptionRow.js @@ -76,8 +76,9 @@ const propTypes = { /** Whether to wrap large text up to 2 lines */ isMultilineSupported: PropTypes.bool, - style: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]), + /** Key used internally by React */ keyForList: PropTypes.string, + style: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]), ...withLocalizePropTypes, }; From 2f7c2b2caa32002455165965d140230a38e3f18c Mon Sep 17 00:00:00 2001 From: OSBotify Date: Thu, 21 Dec 2023 05:04:41 +0000 Subject: [PATCH 89/98] Update version to 1.4.15-0 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 4 ++-- ios/NewExpensifyTests/Info.plist | 4 ++-- package-lock.json | 4 ++-- package.json | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 981800af5b9f..98133994a3e3 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -91,8 +91,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001041406 - versionName "1.4.14-6" + versionCode 1001041500 + versionName "1.4.15-0" } flavorDimensions "default" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 330868b262c4..ce90808c0c44 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.4.14 + 1.4.15 CFBundleSignature ???? CFBundleURLTypes @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.14.6 + 1.4.15.0 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 8af46472572d..6f30fbec8f41 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -15,10 +15,10 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 1.4.14 + 1.4.15 CFBundleSignature ???? CFBundleVersion - 1.4.14.6 + 1.4.15.0 diff --git a/package-lock.json b/package-lock.json index 39cfdfa82854..10f23f7ca4e4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.14-6", + "version": "1.4.15-0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.14-6", + "version": "1.4.15-0", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 3e63b96af229..d430203bedc9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.14-6", + "version": "1.4.15-0", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From 563df39fcaabe4e77ed69fe540f6bc14af1c0f5c Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 21 Dec 2023 09:40:25 +0100 Subject: [PATCH 90/98] fix: initialize state with empty objects --- src/pages/SearchPage.js | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index d779ab856fb5..a2b955755121 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -45,18 +45,10 @@ const defaultProps = { function SearchPage({betas, personalDetails, reports, isSearchingForReports}) { const [searchValue, setSearchValue] = useState(''); - const [searchOptions, setSearchOptions] = useState(() => { - const { - recentReports: localRecentReports, - personalDetails: localPersonalDetails, - userToInvite: localUserToInvite, - } = OptionsListUtils.getSearchOptions(reports, personalDetails, '', betas); - - return { - recentReports: localRecentReports, - personalDetails: localPersonalDetails, - userToInvite: localUserToInvite, - }; + const [searchOptions, setSearchOptions] = useState({ + recentReports: {}, + personalDetails: {}, + userToInvite: {}, }); const {isOffline} = useNetwork(); @@ -179,7 +171,7 @@ function SearchPage({betas, personalDetails, reports, isSearchingForReports}) { > {({didScreenTransitionEnd, safeAreaPaddingBottomStyle}) => ( <> - + Date: Thu, 21 Dec 2023 09:43:26 +0100 Subject: [PATCH 91/98] fix: run prettier --- src/pages/SearchPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index a2b955755121..8fa733903a43 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -171,7 +171,7 @@ function SearchPage({betas, personalDetails, reports, isSearchingForReports}) { > {({didScreenTransitionEnd, safeAreaPaddingBottomStyle}) => ( <> - + Date: Thu, 21 Dec 2023 09:27:04 +0000 Subject: [PATCH 92/98] Update version to 1.4.15-1 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 98133994a3e3..164a548ff7b0 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -91,8 +91,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001041500 - versionName "1.4.15-0" + versionCode 1001041501 + versionName "1.4.15-1" } flavorDimensions "default" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index ce90808c0c44..6a658b888abd 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.15.0 + 1.4.15.1 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 6f30fbec8f41..0cb84e5d01f9 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.4.15.0 + 1.4.15.1 diff --git a/package-lock.json b/package-lock.json index 10f23f7ca4e4..571b91d2036e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.15-0", + "version": "1.4.15-1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.15-0", + "version": "1.4.15-1", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index d430203bedc9..f91043f50dd9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.15-0", + "version": "1.4.15-1", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From f232241b4c95dbb6e8b41c984b24aa25d791fe5d Mon Sep 17 00:00:00 2001 From: OSBotify Date: Thu, 21 Dec 2023 09:43:22 +0000 Subject: [PATCH 93/98] Update version to 1.4.15-2 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 164a548ff7b0..6b6c7442d6c6 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -91,8 +91,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001041501 - versionName "1.4.15-1" + versionCode 1001041502 + versionName "1.4.15-2" } flavorDimensions "default" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 6a658b888abd..c4eff14f35e9 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.15.1 + 1.4.15.2 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 0cb84e5d01f9..2db2550b72ea 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.4.15.1 + 1.4.15.2 diff --git a/package-lock.json b/package-lock.json index 571b91d2036e..0683a7ea31a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.15-1", + "version": "1.4.15-2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.15-1", + "version": "1.4.15-2", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index f91043f50dd9..c17c4a12aea8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.15-1", + "version": "1.4.15-2", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From 880254c55ea2c210f11d77974cc29e4c446f47bd Mon Sep 17 00:00:00 2001 From: OSBotify Date: Thu, 21 Dec 2023 09:53:04 +0000 Subject: [PATCH 94/98] Update version to 1.4.15-3 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 6b6c7442d6c6..9d125fed9d84 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -91,8 +91,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001041502 - versionName "1.4.15-2" + versionCode 1001041503 + versionName "1.4.15-3" } flavorDimensions "default" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index c4eff14f35e9..b1c9e18eafc3 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.15.2 + 1.4.15.3 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 2db2550b72ea..8fa14943572e 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.4.15.2 + 1.4.15.3 diff --git a/package-lock.json b/package-lock.json index 0683a7ea31a4..0558ed46e0a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.15-2", + "version": "1.4.15-3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.15-2", + "version": "1.4.15-3", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index c17c4a12aea8..7c7124c6b41c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.15-2", + "version": "1.4.15-3", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From ad634d118649df1d195afde806317fbc841521e5 Mon Sep 17 00:00:00 2001 From: Vit Horacek Date: Thu, 21 Dec 2023 11:16:08 +0100 Subject: [PATCH 95/98] Revert "Merge pull request #32473 from software-mansion-labs/ideal-nav-lhp" This reverts commit 648c000bcc1d4299032a806e217b83f324be1f7e, reversing changes made to f017c6b582dccec95b5a4e6a9554cca1b8f9f381. --- src/NAVIGATORS.ts | 1 - src/SCREENS.ts | 4 +- .../Navigation/AppNavigator/AuthScreens.tsx | 7 -- .../Navigators/LeftModalNavigator.tsx | 45 ------------ .../AppNavigator/Navigators/Overlay.tsx | 7 +- .../Navigators/RightModalNavigator.tsx | 8 ++- ...orScreenOptions.ts => RHPScreenOptions.ts} | 6 +- .../getRootNavigatorScreenOptions.ts | 18 ----- .../modalCardStyleInterpolator.ts | 9 +-- src/libs/Navigation/Navigation.ts | 57 ++++++++++++--- src/libs/Navigation/dismissModal.ts | 56 --------------- src/libs/Navigation/linkTo.ts | 17 +---- src/libs/Navigation/linkingConfig.ts | 15 ++-- src/libs/Navigation/types.ts | 7 +- src/pages/SearchPage.js | 69 ++++++++++--------- src/styles/index.ts | 37 +--------- 16 files changed, 110 insertions(+), 253 deletions(-) delete mode 100644 src/libs/Navigation/AppNavigator/Navigators/LeftModalNavigator.tsx rename src/libs/Navigation/AppNavigator/{ModalNavigatorScreenOptions.ts => RHPScreenOptions.ts} (68%) delete mode 100644 src/libs/Navigation/dismissModal.ts diff --git a/src/NAVIGATORS.ts b/src/NAVIGATORS.ts index c68a950d3501..a3a041e65684 100644 --- a/src/NAVIGATORS.ts +++ b/src/NAVIGATORS.ts @@ -4,7 +4,6 @@ * */ export default { CENTRAL_PANE_NAVIGATOR: 'CentralPaneNavigator', - LEFT_MODAL_NAVIGATOR: 'LeftModalNavigator', RIGHT_MODAL_NAVIGATOR: 'RightModalNavigator', FULL_SCREEN_NAVIGATOR: 'FullScreenNavigator', } as const; diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 9e52ea0a38ca..2cd263237866 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -81,12 +81,10 @@ const SCREENS = { SAVE_THE_WORLD: { ROOT: 'SaveTheWorld_Root', }, - LEFT_MODAL: { - SEARCH: 'Search', - }, RIGHT_MODAL: { SETTINGS: 'Settings', NEW_CHAT: 'NewChat', + SEARCH: 'Search', DETAILS: 'Details', PROFILE: 'Profile', REPORT_DETAILS: 'Report_Details', diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index 369a37e09e60..f6572c84709d 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -36,7 +36,6 @@ import createCustomStackNavigator from './createCustomStackNavigator'; import defaultScreenOptions from './defaultScreenOptions'; import getRootNavigatorScreenOptions from './getRootNavigatorScreenOptions'; import CentralPaneNavigator from './Navigators/CentralPaneNavigator'; -import LeftModalNavigator from './Navigators/LeftModalNavigator'; import RightModalNavigator from './Navigators/RightModalNavigator'; type AuthScreensProps = { @@ -319,12 +318,6 @@ function AuthScreens({lastUpdateIDAppliedToClient, session, lastOpenedPublicRoom component={RightModalNavigator} listeners={modalScreenListeners} /> - ; - -const Stack = createStackNavigator(); - -function LeftModalNavigator({navigation}: LeftModalNavigatorProps) { - const styles = useThemeStyles(); - const {isSmallScreenWidth} = useWindowDimensions(); - const screenOptions = useMemo(() => ModalNavigatorScreenOptions(styles), [styles]); - - return ( - - {!isSmallScreenWidth && ( - - )} - - - - - - - ); -} - -LeftModalNavigator.displayName = 'LeftModalNavigator'; - -export default LeftModalNavigator; diff --git a/src/libs/Navigation/AppNavigator/Navigators/Overlay.tsx b/src/libs/Navigation/AppNavigator/Navigators/Overlay.tsx index a3fe1c657f34..065de8da578b 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/Overlay.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/Overlay.tsx @@ -9,18 +9,15 @@ import CONST from '@src/CONST'; type OverlayProps = { /* Callback to close the modal */ onPress: () => void; - - /* Returns whether a modal is displayed on the left side of the screen. By default, the modal is displayed on the right */ - isModalOnTheLeft?: boolean; }; -function Overlay({onPress, isModalOnTheLeft = false}: OverlayProps) { +function Overlay({onPress}: OverlayProps) { const styles = useThemeStyles(); const {current} = useCardAnimation(); const {translate} = useLocalize(); return ( - + {/* In the latest Electron version buttons can't be both clickable and draggable. That's why we added this workaround. Because of two Pressable components on the desktop app diff --git a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx index d7c31bcae7d9..bd790589c8d1 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx @@ -4,8 +4,8 @@ import {View} from 'react-native'; import NoDropZone from '@components/DragAndDrop/NoDropZone'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import ModalNavigatorScreenOptions from '@libs/Navigation/AppNavigator/ModalNavigatorScreenOptions'; import * as ModalStackNavigators from '@libs/Navigation/AppNavigator/ModalStackNavigators'; +import RHPScreenOptions from '@libs/Navigation/AppNavigator/RHPScreenOptions'; import type {AuthScreensParamList, RightModalNavigatorParamList} from '@navigation/types'; import NAVIGATORS from '@src/NAVIGATORS'; import SCREENS from '@src/SCREENS'; @@ -18,7 +18,7 @@ const Stack = createStackNavigator(); function RightModalNavigator({navigation}: RightModalNavigatorProps) { const styles = useThemeStyles(); const {isSmallScreenWidth} = useWindowDimensions(); - const screenOptions = useMemo(() => ModalNavigatorScreenOptions(styles), [styles]); + const screenOptions = useMemo(() => RHPScreenOptions(styles), [styles]); return ( @@ -33,6 +33,10 @@ function RightModalNavigator({navigation}: RightModalNavigatorProps) { name={SCREENS.RIGHT_MODAL.NEW_CHAT} component={ModalStackNavigators.NewChatModalStackNavigator} /> + ({ +const RHPScreenOptions = (themeStyles: ThemeStyles): StackNavigationOptions => ({ headerShown: false, animationEnabled: true, gestureDirection: 'horizontal', @@ -14,4 +14,4 @@ const ModalNavigatorScreenOptions = (themeStyles: ThemeStyles): StackNavigationO cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS, }); -export default ModalNavigatorScreenOptions; +export default RHPScreenOptions; diff --git a/src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.ts b/src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.ts index c80ae9914347..379c5281b78f 100644 --- a/src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.ts +++ b/src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.ts @@ -15,8 +15,6 @@ const commonScreenOptions: StackNavigationOptions = { animationTypeForReplace: 'push', }; -const SLIDE_LEFT_OUTPUT_RANGE_MULTIPLIER = -1; - export default (isSmallScreenWidth: boolean, themeStyles: ThemeStyles): ScreenOptions => ({ rightModalNavigator: { ...commonScreenOptions, @@ -34,23 +32,7 @@ export default (isSmallScreenWidth: boolean, themeStyles: ThemeStyles): ScreenOp right: 0, }, }, - leftModalNavigator: { - ...commonScreenOptions, - cardStyleInterpolator: (props) => modalCardStyleInterpolator(isSmallScreenWidth, false, props, SLIDE_LEFT_OUTPUT_RANGE_MULTIPLIER), - presentation: 'transparentModal', - - // We want pop in LHP since there are some flows that would work weird otherwise - animationTypeForReplace: 'pop', - cardStyle: { - ...getNavigationModalCardStyle(), - - // This is necessary to cover translated sidebar with overlay. - width: isSmallScreenWidth ? '100%' : '200%', - // LHP should be displayed in place of the sidebar - left: isSmallScreenWidth ? 0 : -variables.sideBarWidth, - }, - }, homeScreen: { title: CONFIG.SITE_TITLE, ...commonScreenOptions, diff --git a/src/libs/Navigation/AppNavigator/modalCardStyleInterpolator.ts b/src/libs/Navigation/AppNavigator/modalCardStyleInterpolator.ts index fd59b02e724d..eff88422cc5c 100644 --- a/src/libs/Navigation/AppNavigator/modalCardStyleInterpolator.ts +++ b/src/libs/Navigation/AppNavigator/modalCardStyleInterpolator.ts @@ -3,16 +3,11 @@ import {Animated} from 'react-native'; import getCardStyles from '@styles/utils/cardStyles'; import variables from '@styles/variables'; -export default ( - isSmallScreenWidth: boolean, - isFullScreenModal: boolean, - {current: {progress}, inverted, layouts: {screen}}: StackCardInterpolationProps, - outputRangeMultiplier = 1, -): StackCardInterpolatedStyle => { +export default (isSmallScreenWidth: boolean, isFullScreenModal: boolean, {current: {progress}, inverted, layouts: {screen}}: StackCardInterpolationProps): StackCardInterpolatedStyle => { const translateX = Animated.multiply( progress.interpolate({ inputRange: [0, 1], - outputRange: [outputRangeMultiplier * (isSmallScreenWidth ? screen.width : variables.sideBarWidth), 0], + outputRange: [isSmallScreenWidth ? screen.width : variables.sideBarWidth, 0], extrapolate: 'clamp', }), inverted, diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts index a3e89a983f98..3552ff9e7410 100644 --- a/src/libs/Navigation/Navigation.ts +++ b/src/libs/Navigation/Navigation.ts @@ -1,17 +1,18 @@ -import {findFocusedRoute} from '@react-navigation/core'; +import {findFocusedRoute, getActionFromState} from '@react-navigation/core'; import {CommonActions, EventArg, getPathFromState, NavigationContainerEventMap, NavigationState, PartialState, StackActions} from '@react-navigation/native'; +import findLastIndex from 'lodash/findLastIndex'; import Log from '@libs/Log'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; import ROUTES, {Route} from '@src/ROUTES'; -import {PROTECTED_SCREENS} from '@src/SCREENS'; -import originalDismissModal from './dismissModal'; +import SCREENS, {PROTECTED_SCREENS} from '@src/SCREENS'; +import getStateFromPath from './getStateFromPath'; import originalGetTopmostReportActionId from './getTopmostReportActionID'; import originalGetTopmostReportId from './getTopmostReportId'; import linkingConfig from './linkingConfig'; import linkTo from './linkTo'; import navigationRef from './navigationRef'; -import {StateOrRoute} from './types'; +import {StackNavigationAction, StateOrRoute} from './types'; let resolveNavigationIsReadyPromise: () => void; const navigationIsReadyPromise = new Promise((resolve) => { @@ -43,9 +44,6 @@ const getTopmostReportId = (state = navigationRef.getState()) => originalGetTopm // Re-exporting the getTopmostReportActionID here to fill in default value for state. The getTopmostReportActionID isn't defined in this file to avoid cyclic dependencies. const getTopmostReportActionId = (state = navigationRef.getState()) => originalGetTopmostReportActionId(state); -// Re-exporting the dismissModal here to fill in default value for navigationRef. The dismissModal isn't defined in this file to avoid cyclic dependencies. -const dismissModal = (targetReportId = '', ref = navigationRef) => originalDismissModal(targetReportId, ref); - /** Method for finding on which index in stack we are. */ function getActiveRouteIndex(stateOrRoute: StateOrRoute, index?: number): number | undefined { if ('routes' in stateOrRoute && stateOrRoute.routes) { @@ -58,7 +56,7 @@ function getActiveRouteIndex(stateOrRoute: StateOrRoute, index?: number): number return getActiveRouteIndex(childActiveRoute, stateOrRoute.state.index ?? 0); } - if ('name' in stateOrRoute && (stateOrRoute.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR || stateOrRoute.name === NAVIGATORS.LEFT_MODAL_NAVIGATOR)) { + if ('name' in stateOrRoute && stateOrRoute.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR) { return 0; } @@ -162,8 +160,8 @@ function goBack(fallbackRoute: Route, shouldEnforceFallback = false, shouldPopTo if (isFirstRouteInNavigator) { const rootState = navigationRef.getRootState(); const lastRoute = rootState.routes.at(-1); - // If the user comes from a different flow (there is more than one route in ModalNavigator) we should go back to the previous flow on UP button press instead of using the fallbackRoute. - if ((lastRoute?.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR || lastRoute?.name === NAVIGATORS.LEFT_MODAL_NAVIGATOR) && (lastRoute.state?.index ?? 0) > 0) { + // If the user comes from a different flow (there is more than one route in RHP) we should go back to the previous flow on UP button press instead of using the fallbackRoute. + if (lastRoute?.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR && (lastRoute.state?.index ?? 0) > 0) { navigationRef.current.goBack(); return; } @@ -202,6 +200,45 @@ function setParams(params: Record, routeKey: string) { }); } +/** + * Dismisses the last modal stack if there is any + * + * @param targetReportID - The reportID to navigate to after dismissing the modal + */ +function dismissModal(targetReportID?: string) { + if (!canNavigate('dismissModal')) { + return; + } + const rootState = navigationRef.getRootState(); + const lastRoute = rootState.routes.at(-1); + switch (lastRoute?.name) { + case NAVIGATORS.RIGHT_MODAL_NAVIGATOR: + case SCREENS.NOT_FOUND: + case SCREENS.REPORT_ATTACHMENTS: + // if we are not in the target report, we need to navigate to it after dismissing the modal + if (targetReportID && targetReportID !== getTopmostReportId(rootState)) { + const state = getStateFromPath(ROUTES.REPORT_WITH_ID.getRoute(targetReportID)); + + const action: StackNavigationAction = getActionFromState(state, linkingConfig.config); + if (action) { + action.type = 'REPLACE'; + navigationRef.current?.dispatch(action); + } + // If not-found page is in the route stack, we need to close it + } else if (targetReportID && rootState.routes.some((route) => route.name === SCREENS.NOT_FOUND)) { + const lastRouteIndex = rootState.routes.length - 1; + const centralRouteIndex = findLastIndex(rootState.routes, (route) => route.name === NAVIGATORS.CENTRAL_PANE_NAVIGATOR); + navigationRef.current?.dispatch({...StackActions.pop(lastRouteIndex - centralRouteIndex), target: rootState.key}); + } else { + navigationRef.current?.dispatch({...StackActions.pop(), target: rootState.key}); + } + break; + default: { + Log.hmmm('[Navigation] dismissModal failed because there is no modal stack to dismiss'); + } + } +} + /** * Returns the current active route without the URL params */ diff --git a/src/libs/Navigation/dismissModal.ts b/src/libs/Navigation/dismissModal.ts deleted file mode 100644 index 37b4c6d9b9e6..000000000000 --- a/src/libs/Navigation/dismissModal.ts +++ /dev/null @@ -1,56 +0,0 @@ -import {getActionFromState} from '@react-navigation/core'; -import {NavigationContainerRef, StackActions} from '@react-navigation/native'; -import {findLastIndex} from 'lodash'; -import Log from '@libs/Log'; -import NAVIGATORS from '@src/NAVIGATORS'; -import ROUTES from '@src/ROUTES'; -import SCREENS from '@src/SCREENS'; -import getStateFromPath from './getStateFromPath'; -import getTopmostReportId from './getTopmostReportId'; -import linkingConfig from './linkingConfig'; -import {RootStackParamList, StackNavigationAction} from './types'; - -// This function is in a separate file than Navigation.js to avoid cyclic dependency. - -/** - * Dismisses the last modal stack if there is any - * - * @param targetReportID - The reportID to navigate to after dismissing the modal - */ -function dismissModal(targetReportID: string, navigationRef: NavigationContainerRef) { - if (!navigationRef.isReady()) { - return; - } - - const state = navigationRef.getState(); - const lastRoute = state.routes.at(-1); - switch (lastRoute?.name) { - case NAVIGATORS.LEFT_MODAL_NAVIGATOR: - case NAVIGATORS.RIGHT_MODAL_NAVIGATOR: - case SCREENS.NOT_FOUND: - case SCREENS.REPORT_ATTACHMENTS: - // if we are not in the target report, we need to navigate to it after dismissing the modal - if (targetReportID && targetReportID !== getTopmostReportId(state)) { - const reportState = getStateFromPath(ROUTES.REPORT_WITH_ID.getRoute(targetReportID)); - - const action: StackNavigationAction = getActionFromState(reportState, linkingConfig.config); - if (action) { - action.type = 'REPLACE'; - navigationRef.dispatch(action); - } - // If not-found page is in the route stack, we need to close it - } else if (targetReportID && state.routes.some((route) => route.name === SCREENS.NOT_FOUND)) { - const lastRouteIndex = state.routes.length - 1; - const centralRouteIndex = findLastIndex(state.routes, (route) => route.name === NAVIGATORS.CENTRAL_PANE_NAVIGATOR); - navigationRef.dispatch({...StackActions.pop(lastRouteIndex - centralRouteIndex), target: state.key}); - } else { - navigationRef.dispatch({...StackActions.pop(), target: state.key}); - } - break; - default: { - Log.hmmm('[Navigation] dismissModal failed because there is no modal stack to dismiss'); - } - } -} - -export default dismissModal; diff --git a/src/libs/Navigation/linkTo.ts b/src/libs/Navigation/linkTo.ts index 86558765a6e6..9694879f9aae 100644 --- a/src/libs/Navigation/linkTo.ts +++ b/src/libs/Navigation/linkTo.ts @@ -4,7 +4,6 @@ import {Writable} from 'type-fest'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; import {Route} from '@src/ROUTES'; -import dismissModal from './dismissModal'; import getStateFromPath from './getStateFromPath'; import getTopmostReportId from './getTopmostReportId'; import linkingConfig from './linkingConfig'; @@ -56,10 +55,6 @@ function getMinimalAction(action: NavigationAction, state: NavigationState): Wri return currentAction; } -function isModalNavigator(targetNavigator?: string) { - return targetNavigator === NAVIGATORS.LEFT_MODAL_NAVIGATOR || targetNavigator === NAVIGATORS.RIGHT_MODAL_NAVIGATOR; -} - export default function linkTo(navigation: NavigationContainerRef | null, path: Route, type?: string, isActiveRoute?: boolean) { if (!navigation) { throw new Error("Couldn't find a navigation object. Is your component inside a screen in a navigator?"); @@ -80,9 +75,6 @@ export default function linkTo(navigation: NavigationContainerRef = { }, }, [SCREENS.NOT_FOUND]: '*', - [NAVIGATORS.LEFT_MODAL_NAVIGATOR]: { - screens: { - [SCREENS.LEFT_MODAL.SEARCH]: { - screens: { - [SCREENS.SEARCH_ROOT]: ROUTES.SEARCH, - }, - }, - }, - }, + [NAVIGATORS.RIGHT_MODAL_NAVIGATOR]: { screens: { [SCREENS.RIGHT_MODAL.SETTINGS]: { @@ -349,6 +341,11 @@ const linkingConfig: LinkingOptions = { [SCREENS.I_AM_A_TEACHER]: ROUTES.I_AM_A_TEACHER, }, }, + [SCREENS.RIGHT_MODAL.SEARCH]: { + screens: { + [SCREENS.SEARCH_ROOT]: ROUTES.SEARCH, + }, + }, [SCREENS.RIGHT_MODAL.DETAILS]: { screens: { [SCREENS.DETAILS_ROOT]: ROUTES.DETAILS.route, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 95d5c0f7ff15..7f13640f0e9b 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -334,13 +334,10 @@ type PrivateNotesNavigatorParamList = { }; }; -type LeftModalNavigatorParamList = { - [SCREENS.LEFT_MODAL.SEARCH]: NavigatorScreenParams; -}; - type RightModalNavigatorParamList = { [SCREENS.RIGHT_MODAL.SETTINGS]: NavigatorScreenParams; [SCREENS.RIGHT_MODAL.NEW_CHAT]: NavigatorScreenParams; + [SCREENS.RIGHT_MODAL.SEARCH]: NavigatorScreenParams; [SCREENS.RIGHT_MODAL.DETAILS]: NavigatorScreenParams; [SCREENS.RIGHT_MODAL.PROFILE]: NavigatorScreenParams; [SCREENS.RIGHT_MODAL.REPORT_DETAILS]: NavigatorScreenParams; @@ -410,7 +407,6 @@ type AuthScreensParamList = { name: string; }; [SCREENS.NOT_FOUND]: undefined; - [NAVIGATORS.LEFT_MODAL_NAVIGATOR]: NavigatorScreenParams; [NAVIGATORS.RIGHT_MODAL_NAVIGATOR]: NavigatorScreenParams; [SCREENS.DESKTOP_SIGN_IN_REDIRECT]: undefined; [CONST.DEMO_PAGES.MONEY2020]: undefined; @@ -427,7 +423,6 @@ export type { NavigationStateRoute, NavigationRoot, AuthScreensParamList, - LeftModalNavigatorParamList, RightModalNavigatorParamList, PublicScreensParamList, MoneyRequestNavigatorParamList, diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index 8fa733903a43..ab71cfbf9774 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -163,39 +163,42 @@ function SearchPage({betas, personalDetails, reports, isSearchingForReports}) { searchValue, ); - return ( - - {({didScreenTransitionEnd, safeAreaPaddingBottomStyle}) => ( - <> - - - - - - )} - - ); + return ( + + {({didScreenTransitionEnd, safeAreaPaddingBottomStyle}) => ( + <> + + + + + + )} + + ); + } } SearchPage.propTypes = propTypes; diff --git a/src/styles/index.ts b/src/styles/index.ts index 905c25f4f7d8..ae2cf3937ac3 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -1391,36 +1391,6 @@ const styles = (theme: ThemeColors) => textDecorationLine: 'none', }, - breadcrumb: { - color: theme.textSupporting, - fontSize: variables.fontSizeh1, - lineHeight: variables.lineHeightSizeh1, - ...headlineFont, - }, - - breadcrumbStrong: { - color: theme.text, - fontSize: variables.fontSizeXLarge, - }, - - breadcrumbSeparator: { - color: theme.icon, - fontSize: variables.fontSizeXLarge, - lineHeight: variables.lineHeightSizeh1, - ...headlineFont, - }, - - breadcrumbLogo: { - top: 1.66, // Pixel-perfect alignment due to a small difference between logo height and breadcrumb text height - height: variables.lineHeightSizeh1, - }, - - LHPNavigatorContainer: (isSmallScreenWidth: boolean) => - ({ - ...modalNavigatorContainer(isSmallScreenWidth), - left: 0, - } satisfies ViewStyle), - RHPNavigatorContainer: (isSmallScreenWidth: boolean) => ({ ...modalNavigatorContainer(isSmallScreenWidth), @@ -1640,15 +1610,14 @@ const styles = (theme: ThemeColors) => marginBottom: 4, }, - overlayStyles: (current: OverlayStylesParams, isModalOnTheLeft: boolean) => + overlayStyles: (current: OverlayStylesParams) => ({ ...positioning.pFixed, // We need to stretch the overlay to cover the sidebar and the translate animation distance. - // The overlay must also cover borderRadius of the LHP component - left: isModalOnTheLeft ? 0 : -2 * variables.sideBarWidth, + left: -2 * variables.sideBarWidth, top: 0, bottom: 0, - right: isModalOnTheLeft ? -2 * variables.sideBarWidth : 0, + right: 0, backgroundColor: theme.overlay, opacity: current.progress.interpolate({ inputRange: [0, 1], From dbb59d036623f28917acd38a13a4daada373e7a7 Mon Sep 17 00:00:00 2001 From: Vit Horacek Date: Thu, 21 Dec 2023 11:20:13 +0100 Subject: [PATCH 96/98] Use functional component --- src/pages/SearchPage.js | 69 ++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 36 deletions(-) diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index ab71cfbf9774..8fa733903a43 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -163,42 +163,39 @@ function SearchPage({betas, personalDetails, reports, isSearchingForReports}) { searchValue, ); - return ( - - {({didScreenTransitionEnd, safeAreaPaddingBottomStyle}) => ( - <> - - - - - - )} - - ); - } + return ( + + {({didScreenTransitionEnd, safeAreaPaddingBottomStyle}) => ( + <> + + + + + + )} + + ); } SearchPage.propTypes = propTypes; From b5211eb8cd0686d3f36e29f7e855cbd4cd2cde58 Mon Sep 17 00:00:00 2001 From: Vit Horacek Date: Thu, 21 Dec 2023 11:32:12 +0100 Subject: [PATCH 97/98] Add back breadcrumb styles --- src/styles/index.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/styles/index.ts b/src/styles/index.ts index ae2cf3937ac3..67be7cce1642 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -1391,6 +1391,30 @@ const styles = (theme: ThemeColors) => textDecorationLine: 'none', }, + breadcrumb: { + color: theme.textSupporting, + fontSize: variables.fontSizeh1, + lineHeight: variables.lineHeightSizeh1, + ...headlineFont, + }, + + breadcrumbStrong: { + color: theme.text, + fontSize: variables.fontSizeXLarge, + }, + + breadcrumbSeparator: { + color: theme.icon, + fontSize: variables.fontSizeXLarge, + lineHeight: variables.lineHeightSizeh1, + ...headlineFont, + }, + + breadcrumbLogo: { + top: 1.66, // Pixel-perfect alignment due to a small difference between logo height and breadcrumb text height + height: variables.lineHeightSizeh1, + }, + RHPNavigatorContainer: (isSmallScreenWidth: boolean) => ({ ...modalNavigatorContainer(isSmallScreenWidth), From 10dd12ee3efecef191be9cad94f84b3de63315a5 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Thu, 21 Dec 2023 11:31:54 +0000 Subject: [PATCH 98/98] Update version to 1.4.15-4 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 9d125fed9d84..6c86881132cc 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -91,8 +91,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001041503 - versionName "1.4.15-3" + versionCode 1001041504 + versionName "1.4.15-4" } flavorDimensions "default" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index b1c9e18eafc3..f2d76d22b9d5 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.15.3 + 1.4.15.4 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 8fa14943572e..95776b76ffea 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.4.15.3 + 1.4.15.4 diff --git a/package-lock.json b/package-lock.json index 0558ed46e0a6..06a14c3e5ec6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.15-3", + "version": "1.4.15-4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.15-3", + "version": "1.4.15-4", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 7c7124c6b41c..48cdd6563447 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.15-3", + "version": "1.4.15-4", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",