Skip to content

Commit

Permalink
Merge pull request #40650 from ZhenjaHorbach/error-appears-briefly-wh…
Browse files Browse the repository at this point in the history
…en-enabling-features

Not here error appears briefly when enabling features
  • Loading branch information
puneetlath authored Apr 24, 2024
2 parents b0ec7b1 + 7752289 commit 7ddb044
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 12 deletions.
12 changes: 9 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 @@ -32,6 +32,12 @@ function Switch({isOn, onToggle, accessibilityLabel, disabled}: SwitchProps) {
const offsetX = useRef(new Animated.Value(isOn ? OFFSET_X.ON : OFFSET_X.OFF));
const theme = useTheme();

const handleSwitchPress = () => {
InteractionManager.runAfterInteractions(() => {
onToggle(!isOn);
});
};

useEffect(() => {
Animated.timing(offsetX.current, {
toValue: isOn ? OFFSET_X.ON : OFFSET_X.OFF,
Expand All @@ -44,8 +50,8 @@ function Switch({isOn, onToggle, accessibilityLabel, disabled}: SwitchProps) {
<PressableWithFeedback
disabled={disabled}
style={[styles.switchTrack, !isOn && styles.switchInactive]}
onPress={() => onToggle(!isOn)}
onLongPress={() => onToggle(!isOn)}
onPress={handleSwitchPress}
onLongPress={handleSwitchPress}
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 @@ -3810,7 +3810,9 @@ function navigateWhenEnableFeature(policyID: string, featureRoute: Route) {
new Promise<void>((resolve) => {
resolve();
}).then(() => {
Navigation.navigate(featureRoute);
requestAnimationFrame(() => {
Navigation.navigate(featureRoute);
});
});
}

Expand Down
21 changes: 19 additions & 2 deletions src/pages/workspace/FeatureEnabledAccessOrNotFoundWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
/* eslint-disable rulesdir/no-negated-variables */
import React, {useEffect} from 'react';
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 @@ -33,9 +34,25 @@ type FeatureEnabledAccessOrNotFoundComponentProps = FeatureEnabledAccessOrNotFou
};

function FeatureEnabledAccessOrNotFoundComponent(props: FeatureEnabledAccessOrNotFoundComponentProps) {
const pendingField = props.policy?.pendingFields?.[props.featureName];
const isPolicyIDInRoute = !!props.policyID?.length;
const isFeatureEnabled = PolicyUtils.isPolicyFeatureEnabled(props.policy, props.featureName);

const [isPolicyFeatureEnabled, setIsPolicyFeatureEnabled] = useState(isFeatureEnabled);
const {isOffline} = useNetwork();

const shouldShowNotFoundPage = isEmptyObject(props.policy) || !props.policy?.id || !isPolicyFeatureEnabled;
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);

// We only update the feature state if it isn't pending.
// This is because the feature state changes several times during the creation of a workspace, while we are waiting for a response from the backend.
// Without this, we can have unexpectedly have 'Not Found' be shown.
useEffect(() => {
if (pendingField && !isOffline && !isFeatureEnabled) {
return;
}
setIsPolicyFeatureEnabled(isFeatureEnabled);
}, [pendingField, isOffline, isFeatureEnabled]);

useEffect(() => {
if (!isPolicyIDInRoute || !isEmptyObject(props.policy)) {
Expand Down
48 changes: 42 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 @@ -71,6 +73,8 @@ type WorkspaceInitialPageOnyxProps = {

type WorkspaceInitialPageProps = WithPolicyAndFullscreenLoadingProps & WorkspaceInitialPageOnyxProps & StackScreenProps<FullScreenNavigatorParamList, typeof SCREENS.WORKSPACE.INITIAL>;

type PolicyFeatureStates = Record<PolicyFeatureName, boolean>;

function dismissError(policyID: string) {
PolicyUtils.goBackFromInvalidPolicy();
Policy.removeWorkspace(policyID);
Expand All @@ -86,6 +90,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 PolicyFeatureStates;

const policyID = policy?.id ?? '';
const policyName = policy?.name ?? '';
Expand Down Expand Up @@ -122,6 +140,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, reimbursementAcc
const shouldShowProtectedItems = PolicyUtils.isPolicyAdmin(policy);
const isPaidGroupPolicy = PolicyUtils.isPaidGroupPolicy(policy);
const isFreeGroupPolicy = PolicyUtils.isFreeGroupPolicy(policy);
const [featureStates, setFeatureStates] = useState(policyFeatureStates);

const protectedFreePolicyMenuItems: WorkspaceMenuItem[] = [
{
Expand Down Expand Up @@ -167,7 +186,24 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, reimbursementAcc

const protectedCollectPolicyMenuItems: WorkspaceMenuItem[] = [];

if (policy?.areDistanceRatesEnabled) {
// We only update feature states if they aren't pending.
// These changes are made to synchronously change feature states along with FeatureEnabledAccessOrNotFoundComponent.
useEffect(() => {
setFeatureStates((currentFeatureStates) => {
const newFeatureStates = {} as PolicyFeatureStates;
(Object.keys(policy?.pendingFields ?? {}) as PolicyFeatureName[]).forEach((key) => {
const isFeatureEnabled = PolicyUtils.isPolicyFeatureEnabled(policy, key);
newFeatureStates[key] =
prevPendingFields?.[key] !== policy?.pendingFields?.[key] || isOffline || !policy?.pendingFields?.[key] ? isFeatureEnabled : 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 +212,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 +222,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 +232,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 +241,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 +251,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

0 comments on commit 7ddb044

Please sign in to comment.