Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions src/components/Switch.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, {useEffect, useRef} from 'react';
import {Animated} from 'react-native';
import {Animated, InteractionManager} from 'react-native';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import useNativeDriver from '@libs/useNativeDriver';
Expand Down Expand Up @@ -44,8 +44,16 @@ function Switch({isOn, onToggle, accessibilityLabel, disabled}: SwitchProps) {
<PressableWithFeedback
disabled={disabled}
style={[styles.switchTrack, !isOn && styles.switchInactive]}
onPress={() => onToggle(!isOn)}
onLongPress={() => onToggle(!isOn)}
onPress={() => {
InteractionManager.runAfterInteractions(() => {
onToggle(!isOn);
});
}}
onLongPress={() => {
InteractionManager.runAfterInteractions(() => {
onToggle(!isOn);
});
}}
role={CONST.ROLE.SWITCH}
aria-checked={isOn}
accessibilityLabel={accessibilityLabel}
Expand Down
4 changes: 3 additions & 1 deletion src/libs/actions/Policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3806,7 +3806,9 @@ function navigateWhenEnableFeature(policyID: string, featureRoute: Route) {
new Promise<void>((resolve) => {
resolve();
}).then(() => {
Navigation.navigate(featureRoute);
requestAnimationFrame(() => {
Navigation.navigate(featureRoute);
});
});
}

Expand Down
30 changes: 28 additions & 2 deletions src/pages/workspace/FeatureEnabledAccessOrNotFoundWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
/* eslint-disable rulesdir/no-negated-variables */
import React, {useEffect} from 'react';
import {useIsFocused} from '@react-navigation/native';
import React, {useEffect, useState} from 'react';
import type {OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView';
import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
import useNetwork from '@hooks/useNetwork';
import Navigation from '@libs/Navigation/Navigation';
import * as PolicyUtils from '@libs/PolicyUtils';
import * as Policy from '@userActions/Policy';
Expand Down Expand Up @@ -35,7 +37,31 @@ type FeatureEnabledAccessOrNotFoundComponentProps = FeatureEnabledAccessOrNotFou
function FeatureEnabledAccessOrNotFoundComponent(props: FeatureEnabledAccessOrNotFoundComponentProps) {
const isPolicyIDInRoute = !!props.policyID?.length;
const shouldShowFullScreenLoadingIndicator = props.isLoadingReportData !== false && (!Object.entries(props.policy ?? {}).length || !props.policy?.id);
const shouldShowNotFoundPage = isEmptyObject(props.policy) || !props.policy?.id || !PolicyUtils.isPolicyFeatureEnabled(props.policy, props.featureName);
const isFeatureEnabled = PolicyUtils.isPolicyFeatureEnabled(props.policy, props.featureName);
const [isPolicyFeatureEnabled, setIsPolicyFeatureEnabled] = useState(isFeatureEnabled);
const shouldShowNotFoundPage = isEmptyObject(props.policy) || !props.policy?.id || !isPolicyFeatureEnabled;
const pendingField = props.policy?.pendingFields?.[props.featureName];
const [isFeatureScreenOpen, setIsFeatureScreenOpen] = useState(false);
const isFocused = useIsFocused();
const {isOffline} = useNetwork();

useEffect(() => {
if (!isFeatureScreenOpen && isFocused) {
setIsFeatureScreenOpen(true);
setIsPolicyFeatureEnabled(isFeatureEnabled);
return;
}
if (!isFocused) {
setIsFeatureScreenOpen(false);
return;
}
setIsPolicyFeatureEnabled((isPrevFeatureEnabled) => {
if (!pendingField || isOffline) {
return isFeatureEnabled;
}
return isPrevFeatureEnabled;
});
}, [isFocused, pendingField, isOffline, isFeatureEnabled, isFeatureScreenOpen]);

useEffect(() => {
if (!isPolicyIDInRoute || !isEmptyObject(props.policy)) {
Expand Down
51 changes: 45 additions & 6 deletions src/pages/workspace/WorkspaceInitialPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import OfflineWithFeedback from '@components/OfflineWithFeedback';
import ScreenWrapper from '@components/ScreenWrapper';
import ScrollView from '@components/ScrollView';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import usePermissions from '@hooks/usePermissions';
import usePrevious from '@hooks/usePrevious';
import useSingleExecution from '@hooks/useSingleExecution';
Expand All @@ -33,6 +34,7 @@ import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import SCREENS from '@src/SCREENS';
import type * as OnyxTypes from '@src/types/onyx';
import type {PolicyFeatureName} from '@src/types/onyx/Policy';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import type IconAsset from '@src/types/utils/IconAsset';
import type {WithPolicyAndFullscreenLoadingProps} from './withPolicyAndFullscreenLoading';
Expand Down Expand Up @@ -86,6 +88,20 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, reimbursementAcc
const activeRoute = useNavigationState(getTopmostWorkspacesCentralPaneName);
const {translate} = useLocalize();
const {canUseAccountingIntegrations} = usePermissions();
const {isOffline} = useNetwork();

const prevPendingFields = usePrevious(policy?.pendingFields);
const policyFeatureStates = useMemo(
() => ({
[CONST.POLICY.MORE_FEATURES.ARE_DISTANCE_RATES_ENABLED]: policy?.areDistanceRatesEnabled,
[CONST.POLICY.MORE_FEATURES.ARE_WORKFLOWS_ENABLED]: policy?.areWorkflowsEnabled,
[CONST.POLICY.MORE_FEATURES.ARE_CATEGORIES_ENABLED]: policy?.areCategoriesEnabled,
[CONST.POLICY.MORE_FEATURES.ARE_TAGS_ENABLED]: policy?.areTagsEnabled,
[CONST.POLICY.MORE_FEATURES.ARE_TAXES_ENABLED]: policy?.tax?.trackingEnabled,
[CONST.POLICY.MORE_FEATURES.ARE_CONNECTIONS_ENABLED]: policy?.areConnectionsEnabled,
}),
[policy],
) as Record<PolicyFeatureName, boolean>;

const policyID = policy?.id ?? '';
const policyName = policy?.name ?? '';
Expand Down Expand Up @@ -123,6 +139,8 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, reimbursementAcc
const isPaidGroupPolicy = PolicyUtils.isPaidGroupPolicy(policy);
const isFreeGroupPolicy = PolicyUtils.isFreeGroupPolicy(policy);

const [featureStates, setFeatureStates] = useState(policyFeatureStates);

const protectedFreePolicyMenuItems: WorkspaceMenuItem[] = [
{
translationKey: 'workspace.common.card',
Expand Down Expand Up @@ -167,7 +185,28 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, reimbursementAcc

const protectedCollectPolicyMenuItems: WorkspaceMenuItem[] = [];

if (policy?.areDistanceRatesEnabled) {
useEffect(() => {
setFeatureStates((currentFeatureStates) => {
const newFeatureStates = {} as Record<PolicyFeatureName, boolean>;
const keys = Object.keys(policy?.pendingFields ?? {}) as PolicyFeatureName[];
keys.forEach((key) => {
const isFeatureEnabled = PolicyUtils.isPolicyFeatureEnabled(policy, key);
if (prevPendingFields?.[key] !== policy?.pendingFields?.[key] || isOffline || !policy?.pendingFields?.[key]) {
newFeatureStates[key] = isFeatureEnabled;

return;
}

newFeatureStates[key] = currentFeatureStates[key];
});
return {
...policyFeatureStates,
...newFeatureStates,
};
});
}, [policy, isOffline, policyFeatureStates, prevPendingFields]);

if (featureStates?.[CONST.POLICY.MORE_FEATURES.ARE_DISTANCE_RATES_ENABLED]) {
protectedCollectPolicyMenuItems.push({
translationKey: 'workspace.common.distanceRates',
icon: Expensicons.Car,
Expand All @@ -176,7 +215,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, reimbursementAcc
});
}

if (policy?.areWorkflowsEnabled) {
if (featureStates?.[CONST.POLICY.MORE_FEATURES.ARE_WORKFLOWS_ENABLED]) {
protectedCollectPolicyMenuItems.push({
translationKey: 'workspace.common.workflows',
icon: Expensicons.Workflows,
Expand All @@ -186,7 +225,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, reimbursementAcc
});
}

if (policy?.areCategoriesEnabled) {
if (featureStates?.[CONST.POLICY.MORE_FEATURES.ARE_CATEGORIES_ENABLED]) {
protectedCollectPolicyMenuItems.push({
translationKey: 'workspace.common.categories',
icon: Expensicons.Folder,
Expand All @@ -196,7 +235,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, reimbursementAcc
});
}

if (policy?.areTagsEnabled) {
if (featureStates?.[CONST.POLICY.MORE_FEATURES.ARE_TAGS_ENABLED]) {
protectedCollectPolicyMenuItems.push({
translationKey: 'workspace.common.tags',
icon: Expensicons.Tag,
Expand All @@ -205,7 +244,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, reimbursementAcc
});
}

if (policy?.tax?.trackingEnabled) {
if (featureStates?.[CONST.POLICY.MORE_FEATURES.ARE_TAXES_ENABLED]) {
protectedCollectPolicyMenuItems.push({
translationKey: 'workspace.common.taxes',
icon: Expensicons.Tax,
Expand All @@ -215,7 +254,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, reimbursementAcc
});
}

if (policy?.areConnectionsEnabled && canUseAccountingIntegrations) {
if (featureStates?.[CONST.POLICY.MORE_FEATURES.ARE_CONNECTIONS_ENABLED] && canUseAccountingIntegrations) {
protectedCollectPolicyMenuItems.push({
translationKey: 'workspace.common.accounting',
icon: Expensicons.Sync,
Expand Down