Skip to content

Commit

Permalink
Merge branch 'main' into xero-merge-freeze
Browse files Browse the repository at this point in the history
  • Loading branch information
lakchote committed May 30, 2024
2 parents 990ab4f + ea41f1d commit ef55d3a
Show file tree
Hide file tree
Showing 14 changed files with 88 additions and 56 deletions.
4 changes: 2 additions & 2 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
versionCode 1001047703
versionName "1.4.77-3"
versionCode 1001047705
versionName "1.4.77-5"
// Supported language variants must be declared here to avoid from being removed during the compilation.
// This also helps us to not include unnecessary language variants in the APK.
resConfigs "en", "es"
Expand Down
2 changes: 1 addition & 1 deletion ios/NewExpensify/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>1.4.77.3</string>
<string>1.4.77.5</string>
<key>FullStory</key>
<dict>
<key>OrgId</key>
Expand Down
2 changes: 1 addition & 1 deletion ios/NewExpensifyTests/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.4.77.3</string>
<string>1.4.77.5</string>
</dict>
</plist>
2 changes: 1 addition & 1 deletion ios/NotificationServiceExtension/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<key>CFBundleShortVersionString</key>
<string>1.4.77</string>
<key>CFBundleVersion</key>
<string>1.4.77.3</string>
<string>1.4.77.5</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
"version": "1.4.77-3",
"version": "1.4.77-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.",
Expand Down
3 changes: 3 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1596,6 +1596,9 @@ const CONST = {
ACCOUNTANT: 'accountant',
},
},
ACCESS_VARIANTS: {
CREATE: 'create',
},
},

GROWL: {
Expand Down
4 changes: 2 additions & 2 deletions src/components/ConnectionLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
import * as PolicyUtils from '@libs/PolicyUtils';
import type {PolicyAccessVariant} from '@pages/workspace/AccessOrNotFoundWrapper';
import type {AccessVariant} from '@pages/workspace/AccessOrNotFoundWrapper';
import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper';
import type {TranslationPaths} from '@src/languages/types';
import type {ConnectionName, PolicyFeatureName} from '@src/types/onyx/Policy';
Expand Down Expand Up @@ -35,7 +35,7 @@ type ConnectionLayoutProps = {
policyID: string;

/** Defines which types of access should be verified */
accessVariants?: PolicyAccessVariant[];
accessVariants?: AccessVariant[];

/** The current feature name that the user tries to get access to */
featureName?: PolicyFeatureName;
Expand Down
11 changes: 6 additions & 5 deletions src/components/MapView/MapView.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {useFocusEffect, useNavigation} from '@react-navigation/native';
import type {MapState} from '@rnmapbox/maps';
import Mapbox, {MarkerView, setAccessToken} from '@rnmapbox/maps';
import {forwardRef, memo, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react';
import {forwardRef, memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import useThemeStyles from '@hooks/useThemeStyles';
Expand Down Expand Up @@ -30,7 +30,8 @@ const MapView = forwardRef<MapViewHandle, ComponentProps>(

const cameraRef = useRef<Mapbox.Camera>(null);
const [isIdle, setIsIdle] = useState(false);
const [currentPosition, setCurrentPosition] = useState(cachedUserLocation);
const initialLocation = useMemo(() => initialState && {longitude: initialState.location[0], latitude: initialState.location[1]}, [initialState]);
const [currentPosition, setCurrentPosition] = useState(cachedUserLocation ?? initialLocation);
const [userInteractedWithMap, setUserInteractedWithMap] = useState(false);
const shouldInitializeCurrentPosition = useRef(true);

Expand All @@ -42,13 +43,13 @@ const MapView = forwardRef<MapViewHandle, ComponentProps>(

const setCurrentPositionToInitialState: GeolocationErrorCallback = useCallback(
(error) => {
if (error?.code !== GeolocationErrorCode.PERMISSION_DENIED || !initialState) {
if (error?.code !== GeolocationErrorCode.PERMISSION_DENIED || !initialLocation) {
return;
}
UserLocation.clearUserLocation();
setCurrentPosition({longitude: initialState.location[0], latitude: initialState.location[1]});
setCurrentPosition(initialLocation);
},
[initialState],
[initialLocation],
);

useFocusEffect(
Expand Down
11 changes: 6 additions & 5 deletions src/components/MapView/MapView.website.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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, useRef, useState} from 'react';
import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react';
import type {MapRef} from 'react-map-gl';
import Map, {Marker} from 'react-map-gl';
import {View} from 'react-native';
Expand Down Expand Up @@ -52,7 +52,8 @@ const MapView = forwardRef<MapViewHandle, ComponentProps>(
const StyleUtils = useStyleUtils();

const [mapRef, setMapRef] = useState<MapRef | null>(null);
const [currentPosition, setCurrentPosition] = useState(cachedUserLocation);
const initialLocation = useMemo(() => ({longitude: initialState.location[0], latitude: initialState.location[1]}), [initialState]);
const [currentPosition, setCurrentPosition] = useState(cachedUserLocation ?? initialLocation);
const [userInteractedWithMap, setUserInteractedWithMap] = useState(false);
const [shouldResetBoundaries, setShouldResetBoundaries] = useState<boolean>(false);
const setRef = useCallback((newRef: MapRef | null) => setMapRef(newRef), []);
Expand All @@ -66,13 +67,13 @@ const MapView = forwardRef<MapViewHandle, ComponentProps>(

const setCurrentPositionToInitialState: GeolocationErrorCallback = useCallback(
(error) => {
if (error?.code !== GeolocationErrorCode.PERMISSION_DENIED || !initialState) {
if (error?.code !== GeolocationErrorCode.PERMISSION_DENIED || !initialLocation) {
return;
}
UserLocation.clearUserLocation();
setCurrentPosition({longitude: initialState.location[0], latitude: initialState.location[1]});
setCurrentPosition(initialLocation);
},
[initialState],
[initialLocation],
);

useFocusEffect(
Expand Down
4 changes: 2 additions & 2 deletions src/components/SelectionScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {isEmpty} from 'lodash';
import React from 'react';
import useLocalize from '@hooks/useLocalize';
import * as PolicyUtils from '@libs/PolicyUtils';
import type {PolicyAccessVariant} from '@pages/workspace/AccessOrNotFoundWrapper';
import type {AccessVariant} from '@pages/workspace/AccessOrNotFoundWrapper';
import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper';
import type {TranslationPaths} from '@src/languages/types';
import type {ConnectionName, PolicyFeatureName} from '@src/types/onyx/Policy';
Expand Down Expand Up @@ -47,7 +47,7 @@ type SelectionScreenProps = {
policyID: string;

/** Defines which types of access should be verified */
accessVariants?: PolicyAccessVariant[];
accessVariants?: AccessVariant[];

/** The current feature name that the user tries to get access to */
featureName?: PolicyFeatureName;
Expand Down
35 changes: 18 additions & 17 deletions src/pages/iou/request/IOURequestStartPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import React, {useCallback, useEffect, useRef, useState} from 'react';
import {View} from 'react-native';
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView';
import DragAndDropProvider from '@components/DragAndDrop/Provider';
import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
Expand All @@ -13,13 +12,12 @@ import useLocalize from '@hooks/useLocalize';
import usePermissions from '@hooks/usePermissions';
import useThemeStyles from '@hooks/useThemeStyles';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
import * as IOUUtils from '@libs/IOUUtils';
import * as KeyDownPressListener from '@libs/KeyboardShortcut/KeyDownPressListener';
import Navigation from '@libs/Navigation/Navigation';
import OnyxTabNavigator, {TopTab} from '@libs/Navigation/OnyxTabNavigator';
import * as PolicyUtils from '@libs/PolicyUtils';
import * as ReportUtils from '@libs/ReportUtils';
import * as TransactionUtils from '@libs/TransactionUtils';
import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper';
import * as IOU from '@userActions/IOU';
import type {IOURequestType} from '@userActions/IOU';
import CONST from '@src/CONST';
Expand Down Expand Up @@ -105,9 +103,6 @@ function IOURequestStartPage({
const isExpenseReport = ReportUtils.isExpenseReport(report);
const shouldDisplayDistanceRequest = (!!canUseP2PDistanceRequests || isExpenseChat || isExpenseReport || isFromGlobalCreate) && iouType !== CONST.IOU.TYPE.SPLIT;

// Allow the user to submit the expense if we are submitting the expense in global menu or the report can create the exoense
const isAllowedToCreateRequest = isEmptyObject(report?.reportID) || ReportUtils.canCreateRequest(report, policy, iouType) || PolicyUtils.canSendInvoice(allPolicies);

const navigateBack = () => {
Navigation.closeRHPFlow();
};
Expand All @@ -126,15 +121,21 @@ function IOURequestStartPage({
}

return (
<ScreenWrapper
includeSafeAreaPaddingBottom={false}
shouldEnableKeyboardAvoidingView={false}
shouldEnableMinHeight={DeviceCapabilities.canUseTouchScreen()}
headerGapStyles={isDraggingOver ? [styles.receiptDropHeaderGap] : []}
testID={IOURequestStartPage.displayName}
<AccessOrNotFoundWrapper
reportID={reportID}
iouType={iouType}
policyID={policy?.id}
accessVariants={[CONST.IOU.ACCESS_VARIANTS.CREATE]}
allPolicies={allPolicies}
>
{({safeAreaPaddingBottomStyle}) => (
<FullPageNotFoundView shouldShow={!IOUUtils.isValidMoneyRequestType(iouType) || !isAllowedToCreateRequest}>
<ScreenWrapper
includeSafeAreaPaddingBottom={false}
shouldEnableKeyboardAvoidingView={false}
shouldEnableMinHeight={DeviceCapabilities.canUseTouchScreen()}
headerGapStyles={isDraggingOver ? [styles.receiptDropHeaderGap] : []}
testID={IOURequestStartPage.displayName}
>
{({safeAreaPaddingBottomStyle}) => (
<DragAndDropProvider
setIsDraggingOver={setIsDraggingOver}
isDisabled={selectedTab !== CONST.TAB_REQUEST.SCAN}
Expand All @@ -159,9 +160,9 @@ function IOURequestStartPage({
)}
</View>
</DragAndDropProvider>
</FullPageNotFoundView>
)}
</ScreenWrapper>
)}
</ScreenWrapper>
</AccessOrNotFoundWrapper>
);
}

Expand Down
58 changes: 42 additions & 16 deletions src/pages/workspace/AccessOrNotFoundWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
/* eslint-disable rulesdir/no-negated-variables */
import React, {useEffect} from 'react';
import type {OnyxEntry} from 'react-native-onyx';
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import type {FullPageNotFoundViewProps} from '@components/BlockingViews/FullPageNotFoundView';
import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView';
import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
import * as IOUUtils from '@libs/IOUUtils';
import Navigation from '@libs/Navigation/Navigation';
import * as PolicyUtils from '@libs/PolicyUtils';
import * as ReportUtils from '@libs/ReportUtils';
import NotFoundPage from '@pages/ErrorPage/NotFoundPage';
import * as Policy from '@userActions/Policy/Policy';
import type {IOUType} from '@src/CONST';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
Expand All @@ -17,13 +20,22 @@ import type {PolicyFeatureName} from '@src/types/onyx/Policy';
import callOrReturn from '@src/types/utils/callOrReturn';
import {isEmptyObject} from '@src/types/utils/EmptyObject';

const POLICY_ACCESS_VARIANTS = {
const ACCESS_VARIANTS = {
[CONST.POLICY.ACCESS_VARIANTS.PAID]: (policy: OnyxEntry<OnyxTypes.Policy>) => PolicyUtils.isPaidGroupPolicy(policy) && !!policy?.isPolicyExpenseChatEnabled,
[CONST.POLICY.ACCESS_VARIANTS.ADMIN]: (policy: OnyxEntry<OnyxTypes.Policy>) => PolicyUtils.isPolicyAdmin(policy),
} as const satisfies Record<string, (policy: OnyxTypes.Policy) => boolean>;

type PolicyAccessVariant = keyof typeof POLICY_ACCESS_VARIANTS;
[CONST.IOU.ACCESS_VARIANTS.CREATE]: (policy: OnyxEntry<OnyxTypes.Policy>, report: OnyxEntry<OnyxTypes.Report>, allPolicies: OnyxCollection<OnyxTypes.Policy>, iouType?: IOUType) =>
!!iouType &&
IOUUtils.isValidMoneyRequestType(iouType) &&
// Allow the user to submit the expense if we are submitting the expense in global menu or the report can create the expense
(isEmptyObject(report?.reportID) || ReportUtils.canCreateRequest(report, policy, iouType)) &&
(iouType !== CONST.IOU.TYPE.INVOICE || PolicyUtils.canSendInvoice(allPolicies)),
} as const satisfies Record<string, (policy: OnyxTypes.Policy, report: OnyxTypes.Report, allPolicies: OnyxCollection<OnyxTypes.Policy>, iouType?: IOUType) => boolean>;

type AccessVariant = keyof typeof ACCESS_VARIANTS;
type AccessOrNotFoundWrapperOnyxProps = {
/** The report that holds the transaction */
report: OnyxEntry<OnyxTypes.Report>;

/** The report currently being looked at */
policy: OnyxEntry<OnyxTypes.Policy>;

Expand All @@ -35,11 +47,14 @@ type AccessOrNotFoundWrapperProps = AccessOrNotFoundWrapperOnyxProps & {
/** The children to render */
children: ((props: AccessOrNotFoundWrapperOnyxProps) => React.ReactNode) | React.ReactNode;

/** The id of the report that holds the transaction */
reportID?: string;

/** The report currently being looked at */
policyID: string;
policyID?: string;

/** Defines which types of access should be verified */
accessVariants?: PolicyAccessVariant[];
accessVariants?: AccessVariant[];

/** The current feature name that the user tries to get access to */
featureName?: PolicyFeatureName;
Expand All @@ -49,6 +64,12 @@ type AccessOrNotFoundWrapperProps = AccessOrNotFoundWrapperOnyxProps & {

/** Whether or not to block user from accessing the page */
shouldBeBlocked?: boolean;

/** The type of the transaction */
iouType?: IOUType;

/** The list of all policies */
allPolicies?: OnyxCollection<OnyxTypes.Policy>;
} & Pick<FullPageNotFoundViewProps, 'subtitleKey' | 'onLinkPress'>;

type PageNotFoundFallbackProps = Pick<AccessOrNotFoundWrapperProps, 'policyID' | 'fullPageNotFoundViewProps'> & {shouldShowFullScreenFallback: boolean};
Expand All @@ -64,17 +85,19 @@ function PageNotFoundFallback({policyID, shouldShowFullScreenFallback, fullPageN
/>
) : (
<NotFoundPage
onBackButtonPress={() => Navigation.goBack(ROUTES.WORKSPACE_PROFILE.getRoute(policyID))}
onBackButtonPress={() => Navigation.goBack(policyID ? ROUTES.WORKSPACE_PROFILE.getRoute(policyID) : ROUTES.HOME)}
// eslint-disable-next-line react/jsx-props-no-spreading
{...fullPageNotFoundViewProps}
/>
);
}

function AccessOrNotFoundWrapper({accessVariants = [], fullPageNotFoundViewProps, shouldBeBlocked, ...props}: AccessOrNotFoundWrapperProps) {
const {policy, policyID, featureName, isLoadingReportData} = props;
const {policy, policyID, report, iouType, allPolicies, featureName, isLoadingReportData} = props;

const isPolicyIDInRoute = !!policyID?.length;
const isMoneyRequest = !!iouType && IOUUtils.isValidMoneyRequestType(iouType);
const isFromGlobalCreate = isEmptyObject(report?.reportID);

useEffect(() => {
if (!isPolicyIDInRoute || !isEmptyObject(policy)) {
Expand All @@ -86,17 +109,17 @@ function AccessOrNotFoundWrapper({accessVariants = [], fullPageNotFoundViewProps
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isPolicyIDInRoute, policyID]);

const shouldShowFullScreenLoadingIndicator = isLoadingReportData !== false && (!Object.entries(policy ?? {}).length || !policy?.id);
const shouldShowFullScreenLoadingIndicator = !isMoneyRequest && isLoadingReportData !== false && (!Object.entries(policy ?? {}).length || !policy?.id);

const isFeatureEnabled = featureName ? PolicyUtils.isPolicyFeatureEnabled(policy, featureName) : true;

const isPageAccessible = accessVariants.reduce((acc, variant) => {
const accessFunction = POLICY_ACCESS_VARIANTS[variant];
return acc && accessFunction(policy);
const accessFunction = ACCESS_VARIANTS[variant];
return acc && accessFunction(policy, report, allPolicies ?? null, iouType);
}, true);

const shouldShowNotFoundPage =
isEmptyObject(policy) || (Object.keys(policy).length === 1 && !isEmptyObject(policy.errors)) || !policy?.id || !isPageAccessible || !isFeatureEnabled || shouldBeBlocked;
const isPolicyNotAccessible = isEmptyObject(policy) || (Object.keys(policy).length === 1 && !isEmptyObject(policy.errors)) || !policy?.id;
const shouldShowNotFoundPage = (!isMoneyRequest && !isFromGlobalCreate && isPolicyNotAccessible) || !isPageAccessible || !isFeatureEnabled || shouldBeBlocked;

if (shouldShowFullScreenLoadingIndicator) {
return <FullscreenLoadingIndicator />;
Expand All @@ -115,11 +138,14 @@ function AccessOrNotFoundWrapper({accessVariants = [], fullPageNotFoundViewProps
return callOrReturn(props.children, props);
}

export type {PolicyAccessVariant};
export type {AccessVariant};

export default withOnyx<AccessOrNotFoundWrapperProps, AccessOrNotFoundWrapperOnyxProps>({
report: {
key: ({reportID}) => `${ONYXKEYS.COLLECTION.REPORT}${reportID}`,
},
policy: {
key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID ?? ''}`,
key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
},
isLoadingReportData: {
key: ONYXKEYS.IS_LOADING_REPORT_DATA,
Expand Down
Loading

0 comments on commit ef55d3a

Please sign in to comment.