Skip to content

Commit 1fc7d1d

Browse files
authored
Merge pull request Expensify#90371 from Expensify/feat/90248-hide-account-items
feat: Hide most sections for agents in account page
2 parents d8a8d01 + 5729d79 commit 1fc7d1d

8 files changed

Lines changed: 582 additions & 152 deletions

File tree

src/CONST/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6423,6 +6423,7 @@ const CONST = {
64236423
DENIED_ACCESS_VARIANTS: {
64246424
DELEGATE: 'delegate',
64256425
SUBMITTER: 'submitter',
6426+
AGENT: 'agent',
64266427
},
64276428
},
64286429
DELEGATE_ROLE_HELP_DOT_ARTICLE_LINK: 'https://help.expensify.com/expensify-classic/hubs/copilots-and-delegates/',

src/components/DelegateNoAccessWrapper.tsx

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,26 @@ import useOnyx from '@hooks/useOnyx';
44
import useResponsiveLayout from '@hooks/useResponsiveLayout';
55
import AccountUtils from '@libs/AccountUtils';
66
import Navigation from '@libs/Navigation/Navigation';
7+
import {isAgentEmail} from '@libs/SessionUtils';
78
import CONST from '@src/CONST';
89
import ONYXKEYS from '@src/ONYXKEYS';
9-
import type {Account} from '@src/types/onyx';
10+
import type {Account, Session} from '@src/types/onyx';
1011
import callOrReturn from '@src/types/utils/callOrReturn';
1112
import FullPageNotFoundView from './BlockingViews/FullPageNotFoundView';
1213

14+
type AccessContext = {
15+
account: OnyxEntry<Account>;
16+
session: OnyxEntry<Session>;
17+
};
18+
1319
const DENIED_ACCESS_VARIANTS = {
1420
// To Restrict All Delegates From Accessing The Page.
15-
[CONST.DELEGATE.DENIED_ACCESS_VARIANTS.DELEGATE]: (account: OnyxEntry<Account>) => isDelegate(account),
21+
[CONST.DELEGATE.DENIED_ACCESS_VARIANTS.DELEGATE]: ({account}: AccessContext) => isDelegate(account),
1622
// To Restrict Only Limited Access Delegates From Accessing The Page.
17-
[CONST.DELEGATE.DENIED_ACCESS_VARIANTS.SUBMITTER]: (account: OnyxEntry<Account>) => isSubmitter(account),
18-
} as const satisfies Record<string, (account: OnyxEntry<Account>) => boolean>;
23+
[CONST.DELEGATE.DENIED_ACCESS_VARIANTS.SUBMITTER]: ({account}: AccessContext) => isSubmitter(account),
24+
// To Restrict Agent Accounts From Accessing The Page.
25+
[CONST.DELEGATE.DENIED_ACCESS_VARIANTS.AGENT]: ({session}: AccessContext) => isAgentEmail(session?.email),
26+
} as const satisfies Record<string, (context: AccessContext) => boolean>;
1927

2028
type AccessDeniedVariants = keyof typeof DENIED_ACCESS_VARIANTS;
2129

@@ -38,9 +46,10 @@ function isSubmitter(account: OnyxEntry<Account>) {
3846

3947
function DelegateNoAccessWrapper({accessDeniedVariants = [], shouldForceFullScreen, onBackButtonPress, ...props}: DelegateNoAccessWrapperProps) {
4048
const [account] = useOnyx(ONYXKEYS.ACCOUNT);
49+
const [session] = useOnyx(ONYXKEYS.SESSION);
4150
const isPageAccessDenied = accessDeniedVariants.reduce((acc, variant) => {
4251
const accessDeniedFunction = DENIED_ACCESS_VARIANTS[variant];
43-
return acc || accessDeniedFunction(account);
52+
return acc || accessDeniedFunction({account, session});
4453
}, false);
4554
const {shouldUseNarrowLayout} = useResponsiveLayout();
4655

src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx

Lines changed: 182 additions & 116 deletions
Large diffs are not rendered by default.

src/libs/Navigation/AppNavigator/Navigators/SettingsSplitNavigator.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {View} from 'react-native';
44
import FocusTrapForScreens from '@components/FocusTrap/FocusTrapForScreen';
55
import createSplitNavigator from '@libs/Navigation/AppNavigator/createSplitNavigator';
66
import useSplitNavigatorScreenOptions from '@libs/Navigation/AppNavigator/useSplitNavigatorScreenOptions';
7+
import withAgentAccessDenied from '@libs/Navigation/AppNavigator/withAgentAccessDenied';
78
import type {SettingsSplitNavigatorParamList} from '@libs/Navigation/types';
89
import SCREENS from '@src/SCREENS';
910
import type ReactComponentModule from '@src/types/utils/ReactComponentModule';
@@ -13,18 +14,18 @@ const loadInitialSettingsPage = () => require<ReactComponentModule>('../../../..
1314
type Screens = Partial<Record<keyof SettingsSplitNavigatorParamList, () => React.ComponentType>>;
1415

1516
const CENTRAL_PANE_SETTINGS_SCREENS = {
16-
[SCREENS.SETTINGS.PREFERENCES.ROOT]: () => require<ReactComponentModule>('../../../../pages/settings/Preferences/PreferencesPage').default,
17-
[SCREENS.SETTINGS.SECURITY]: () => require<ReactComponentModule>('../../../../pages/settings/Security/SecuritySettingsPage').default,
17+
[SCREENS.SETTINGS.PREFERENCES.ROOT]: withAgentAccessDenied(() => require<ReactComponentModule>('../../../../pages/settings/Preferences/PreferencesPage').default),
18+
[SCREENS.SETTINGS.SECURITY]: withAgentAccessDenied(() => require<ReactComponentModule>('../../../../pages/settings/Security/SecuritySettingsPage').default),
1819
[SCREENS.SETTINGS.COPILOT]: () => require<ReactComponentModule>('../../../../pages/settings/Copilot/CopilotPage').default,
1920
[SCREENS.SETTINGS.PROFILE.ROOT]: () => require<ReactComponentModule>('../../../../pages/settings/Profile/ProfilePage').default,
20-
[SCREENS.SETTINGS.WALLET.ROOT]: () => require<ReactComponentModule>('../../../../pages/settings/Wallet/WalletPage').default,
21-
[SCREENS.SETTINGS.AGENTS.ROOT]: () => require<ReactComponentModule>('../../../../pages/settings/Agents/AgentsPage').default,
21+
[SCREENS.SETTINGS.WALLET.ROOT]: withAgentAccessDenied(() => require<ReactComponentModule>('../../../../pages/settings/Wallet/WalletPage').default),
22+
[SCREENS.SETTINGS.AGENTS.ROOT]: withAgentAccessDenied(() => require<ReactComponentModule>('../../../../pages/settings/Agents/AgentsPage').default),
2223
[SCREENS.SETTINGS.RULES.ROOT]: () => require<ReactComponentModule>('../../../../pages/settings/Rules/ExpenseRulesPage').default,
2324
[SCREENS.SETTINGS.HELP]: () => require<ReactComponentModule>('../../../../pages/settings/HelpPage/HelpPage').default,
2425
[SCREENS.SETTINGS.ABOUT]: () => require<ReactComponentModule>('../../../../pages/settings/AboutPage/AboutPage').default,
2526
[SCREENS.SETTINGS.TROUBLESHOOT]: () => require<ReactComponentModule>('../../../../pages/settings/Troubleshoot/TroubleshootPage').default,
2627
[SCREENS.SETTINGS.SAVE_THE_WORLD]: () => require<ReactComponentModule>('../../../../pages/TeachersUnite/SaveTheWorldPage').default,
27-
[SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: () => require<ReactComponentModule>('../../../../pages/settings/Subscription/SubscriptionSettingsPage').default,
28+
[SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: withAgentAccessDenied(() => require<ReactComponentModule>('../../../../pages/settings/Subscription/SubscriptionSettingsPage').default),
2829
} satisfies Screens;
2930

3031
const Split = createSplitNavigator<SettingsSplitNavigatorParamList>();
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import React from 'react';
2+
import DelegateNoAccessWrapper from '@components/DelegateNoAccessWrapper';
3+
import CONST from '@src/CONST';
4+
5+
function withAgentAccessDenied(getComponent: () => React.ComponentType): () => React.ComponentType {
6+
let ProtectedComponent: React.ComponentType | undefined;
7+
return () => {
8+
if (!ProtectedComponent) {
9+
const Component = getComponent();
10+
ProtectedComponent = (props) => (
11+
<DelegateNoAccessWrapper accessDeniedVariants={[CONST.DELEGATE.DENIED_ACCESS_VARIANTS.AGENT]}>
12+
<Component {...props} />
13+
</DelegateNoAccessWrapper>
14+
);
15+
}
16+
return ProtectedComponent;
17+
};
18+
}
19+
20+
export default withAgentAccessDenied;

src/pages/settings/InitialSettingsPage.tsx

Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import {hasPendingExpensifyCardAction} from '@libs/CardUtils';
4444
import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute';
4545
import useIsSidebarRouteActive from '@libs/Navigation/helpers/useIsSidebarRouteActive';
4646
import Navigation from '@libs/Navigation/Navigation';
47+
import {useIsAgentAccount} from '@libs/SessionUtils';
4748
import {getFreeTrialText, hasSubscriptionRedDotError} from '@libs/SubscriptionUtils';
4849
import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan';
4950
import {shouldHideOldAppRedirect} from '@libs/TryNewDotUtils';
@@ -170,6 +171,7 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr
170171
const [tryNewDot, tryNewDotMetadata] = useOnyx(ONYXKEYS.NVP_TRY_NEW_DOT);
171172
const isLoadingTryNewDot = isLoadingOnyxValue(tryNewDotMetadata);
172173
const {isBetaEnabled} = usePermissions();
174+
const isAgentAccount = useIsAgentAccount();
173175

174176
const freeTrialText = getFreeTrialText(currentUserPersonalDetails.accountID, translate, policies, introSelected, firstDayFreeTrial, lastDayFreeTrial);
175177

@@ -261,47 +263,59 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr
261263
sentryLabel: CONST.SENTRY_LABEL.ACCOUNT.PROFILE,
262264
action: () => Navigation.navigate(ROUTES.SETTINGS_PROFILE.getRoute()),
263265
},
264-
{
265-
translationKey: 'common.wallet',
266-
icon: icons.Wallet,
267-
screenName: SCREENS.SETTINGS.WALLET.ROOT,
268-
brickRoadIndicator: walletBrickRoadIndicator,
269-
sentryLabel: CONST.SENTRY_LABEL.ACCOUNT.WALLET,
270-
action: () => Navigation.navigate(ROUTES.SETTINGS_WALLET),
271-
badgeText: hasActivatedWallet ? convertToDisplayString(userWallet?.currentBalance, CONST.CURRENCY.USD) : undefined,
272-
},
266+
...(!isAgentAccount
267+
? [
268+
{
269+
translationKey: 'common.wallet' as const,
270+
icon: icons.Wallet,
271+
screenName: SCREENS.SETTINGS.WALLET.ROOT,
272+
brickRoadIndicator: walletBrickRoadIndicator,
273+
sentryLabel: CONST.SENTRY_LABEL.ACCOUNT.WALLET,
274+
action: () => Navigation.navigate(ROUTES.SETTINGS_WALLET),
275+
badgeText: hasActivatedWallet ? convertToDisplayString(userWallet?.currentBalance, CONST.CURRENCY.USD) : undefined,
276+
},
277+
]
278+
: []),
273279
{
274280
translationKey: 'expenseRulesPage.title',
275281
icon: icons.Bolt,
276282
screenName: SCREENS.SETTINGS.RULES.ROOT,
277283
sentryLabel: CONST.SENTRY_LABEL.ACCOUNT.RULES,
278284
action: () => Navigation.navigate(ROUTES.SETTINGS_RULES),
279285
},
280-
{
281-
translationKey: 'common.preferences',
282-
icon: icons.Gear,
283-
screenName: SCREENS.SETTINGS.PREFERENCES.ROOT,
284-
sentryLabel: CONST.SENTRY_LABEL.ACCOUNT.PREFERENCES,
285-
action: () => Navigation.navigate(ROUTES.SETTINGS_PREFERENCES),
286-
},
286+
...(!isAgentAccount
287+
? [
288+
{
289+
translationKey: 'common.preferences' as const,
290+
icon: icons.Gear,
291+
screenName: SCREENS.SETTINGS.PREFERENCES.ROOT,
292+
sentryLabel: CONST.SENTRY_LABEL.ACCOUNT.PREFERENCES,
293+
action: () => Navigation.navigate(ROUTES.SETTINGS_PREFERENCES),
294+
},
295+
]
296+
: []),
287297
{
288298
translationKey: 'delegate.copilot',
289299
icon: icons.Users,
290300
screenName: SCREENS.SETTINGS.COPILOT,
291301
sentryLabel: CONST.SENTRY_LABEL.ACCOUNT.COPILOT,
292302
action: () => Navigation.navigate(ROUTES.SETTINGS_COPILOT),
293303
},
294-
{
295-
translationKey: 'initialSettingsPage.security',
296-
icon: icons.Lock,
297-
screenName: SCREENS.SETTINGS.SECURITY,
298-
brickRoadIndicator: securityBrickRoadIndicator,
299-
sentryLabel: CONST.SENTRY_LABEL.ACCOUNT.SECURITY,
300-
action: () => Navigation.navigate(ROUTES.SETTINGS_SECURITY),
301-
},
304+
...(!isAgentAccount
305+
? [
306+
{
307+
translationKey: 'initialSettingsPage.security' as const,
308+
icon: icons.Lock,
309+
screenName: SCREENS.SETTINGS.SECURITY,
310+
brickRoadIndicator: securityBrickRoadIndicator,
311+
sentryLabel: CONST.SENTRY_LABEL.ACCOUNT.SECURITY,
312+
action: () => Navigation.navigate(ROUTES.SETTINGS_SECURITY),
313+
},
314+
]
315+
: []),
302316
];
303317

304-
if (isBetaEnabled(CONST.BETAS.CUSTOM_AGENT)) {
318+
if (!isAgentAccount && isBetaEnabled(CONST.BETAS.CUSTOM_AGENT)) {
305319
const rulesIndex = accountItems.findIndex((item) => item.screenName === SCREENS.SETTINGS.RULES.ROOT);
306320
accountItems.splice(rulesIndex + 1, 0, {
307321
translationKey: 'agentsPage.title',
@@ -314,7 +328,7 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr
314328
});
315329
}
316330

317-
if (subscriptionPlan || (amountOwed ?? 0) > 0) {
331+
if (!isAgentAccount && (subscriptionPlan || (amountOwed ?? 0) > 0)) {
318332
accountItems.splice(1, 0, {
319333
translationKey: 'allSettingsScreen.subscription',
320334
icon: icons.CreditCard,

0 commit comments

Comments
 (0)