From ee12dfd3cc9f61f3c87d5b68836720f88ae0daa7 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Sat, 24 Aug 2024 16:29:32 +0800 Subject: [PATCH 1/2] check user role from employeeList --- .../MoneyRequestConfirmationListFooter.tsx | 5 +++-- src/components/SettlementButton.tsx | 5 ++--- src/libs/PolicyUtils.ts | 22 ++++++++++--------- src/libs/actions/Policy/Policy.ts | 8 +++---- src/pages/WorkspaceSwitcherPage/index.tsx | 5 +++-- .../report/SystemChatReportFooterMessage.tsx | 7 ++++-- .../FloatingActionButtonAndPopover.tsx | 2 +- .../MoneyRequestParticipantsSelector.tsx | 5 +++-- .../request/step/IOURequestStepSendFrom.tsx | 5 +++-- .../workspace/AccessOrNotFoundWrapper.tsx | 2 +- src/pages/workspace/WorkspacesListPage.tsx | 11 +++++----- 11 files changed, 43 insertions(+), 34 deletions(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index 623348a4c7a4..4c3641bc7c59 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -217,6 +217,7 @@ function MoneyRequestConfirmationListFooter({ const {translate, toLocaleDigit} = useLocalize(); const {isOffline} = useNetwork(); const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); + const [currentUserLogin] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.email}); // A flag and a toggler for showing the rest of the form fields const [shouldExpandFields, toggleShouldExpandFields] = useReducer((state) => !state, false); @@ -232,8 +233,8 @@ function MoneyRequestConfirmationListFooter({ const canUpdateSenderWorkspace = useMemo(() => { const isInvoiceRoomParticipant = selectedParticipants.some((participant) => participant.isInvoiceRoom); - return PolicyUtils.canSendInvoice(allPolicies) && !!transaction?.isFromGlobalCreate && !isInvoiceRoomParticipant; - }, [allPolicies, selectedParticipants, transaction?.isFromGlobalCreate]); + return PolicyUtils.canSendInvoice(allPolicies, currentUserLogin) && !!transaction?.isFromGlobalCreate && !isInvoiceRoomParticipant; + }, [allPolicies, currentUserLogin, selectedParticipants, transaction?.isFromGlobalCreate]); const isTypeSend = iouType === CONST.IOU.TYPE.PAY; const taxRates = policy?.taxRates ?? null; diff --git a/src/components/SettlementButton.tsx b/src/components/SettlementButton.tsx index efe7aa91de18..d7dd314a456e 100644 --- a/src/components/SettlementButton.tsx +++ b/src/components/SettlementButton.tsx @@ -158,10 +158,9 @@ function SettlementButton({ const {translate} = useLocalize(); const {isOffline} = useNetwork(); const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID); - - const primaryPolicy = useMemo(() => PolicyActions.getPrimaryPolicy(activePolicyID), [activePolicyID]); - const session = useSession(); + const primaryPolicy = useMemo(() => PolicyActions.getPrimaryPolicy(activePolicyID, session?.email), [activePolicyID, session?.email]); + // The app would crash due to subscribing to the entire report collection if chatReportID is an empty string. So we should have a fallback ID here. const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID || -1}`); const isInvoiceReport = (!isEmptyObject(iouReport) && ReportUtils.isInvoiceReport(iouReport)) || false; diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 3f3a2a96a1e1..0facb6287066 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -159,6 +159,10 @@ function getPolicyBrickRoadIndicatorStatus(policy: OnyxEntry): ValueOf, currentUserLogin: string | undefined) { + return policy?.role ?? policy?.employeeList?.[currentUserLogin ?? '-1']?.role; +} + /** * Check if the policy can be displayed * If offline, always show the policy pending deletion. @@ -166,12 +170,12 @@ function getPolicyBrickRoadIndicatorStatus(policy: OnyxEntry): ValueOf, isOffline: boolean): boolean { +function shouldShowPolicy(policy: OnyxEntry, isOffline: boolean, currentUserLogin: string | undefined): boolean { return ( !!policy && (policy?.type !== CONST.POLICY.TYPE.PERSONAL || !!policy?.isJoinRequestPending) && (isOffline || policy?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || Object.keys(policy.errors ?? {}).length > 0) && - !!policy?.role + !!getPolicyRole(policy, currentUserLogin) ); } @@ -183,14 +187,12 @@ function isExpensifyTeam(email: string | undefined): boolean { /** * Checks if the current user is an admin of the policy. */ -const isPolicyAdmin = (policy: OnyxInputOrEntry, currentUserLogin?: string): boolean => - (policy?.role ?? (currentUserLogin && policy?.employeeList?.[currentUserLogin]?.role)) === CONST.POLICY.ROLE.ADMIN; +const isPolicyAdmin = (policy: OnyxInputOrEntry, currentUserLogin?: string): boolean => getPolicyRole(policy, currentUserLogin) === CONST.POLICY.ROLE.ADMIN; /** * Checks if the current user is of the role "user" on the policy. */ -const isPolicyUser = (policy: OnyxInputOrEntry, currentUserLogin?: string): boolean => - (policy?.role ?? (currentUserLogin && policy?.employeeList?.[currentUserLogin]?.role)) === CONST.POLICY.ROLE.USER; +const isPolicyUser = (policy: OnyxInputOrEntry, currentUserLogin?: string): boolean => getPolicyRole(policy, currentUserLogin) === CONST.POLICY.ROLE.USER; /** * Checks if the policy is a free group policy. @@ -557,9 +559,9 @@ function getPolicy(policyID: string | undefined): OnyxEntry { } /** Return active policies where current user is an admin */ -function getActiveAdminWorkspaces(policies: OnyxCollection | null): Policy[] { +function getActiveAdminWorkspaces(policies: OnyxCollection | null, currentUserLogin: string | undefined): Policy[] { const activePolicies = getActivePolicies(policies); - return activePolicies.filter((policy) => shouldShowPolicy(policy, NetworkStore.isOffline()) && isPolicyAdmin(policy)); + return activePolicies.filter((policy) => shouldShowPolicy(policy, NetworkStore.isOffline(), currentUserLogin) && isPolicyAdmin(policy, currentUserLogin)); } /** Whether the user can send invoice from the workspace */ @@ -569,8 +571,8 @@ function canSendInvoiceFromWorkspace(policyID: string | undefined): boolean { } /** Whether the user can send invoice */ -function canSendInvoice(policies: OnyxCollection | null): boolean { - return getActiveAdminWorkspaces(policies).length > 0; +function canSendInvoice(policies: OnyxCollection | null, currentUserLogin: string | undefined): boolean { + return getActiveAdminWorkspaces(policies, currentUserLogin).length > 0; // TODO: Uncomment the following line when the invoices screen is ready - https://github.com/Expensify/App/issues/45175. // return getActiveAdminWorkspaces(policies).some((policy) => canSendInvoiceFromWorkspace(policy.id)); } diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 753428e4a6df..af52f457487a 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -207,8 +207,8 @@ function getPolicy(policyID: string | undefined): OnyxEntry { * Returns a primary policy for the user */ // TODO: Use getInvoicePrimaryWorkspace when the invoices screen is ready - https://github.com/Expensify/App/issues/45175. -function getPrimaryPolicy(activePolicyID?: OnyxEntry): Policy | undefined { - const activeAdminWorkspaces = PolicyUtils.getActiveAdminWorkspaces(allPolicies); +function getPrimaryPolicy(activePolicyID: OnyxEntry, currentUserLogin: string | undefined): Policy | undefined { + const activeAdminWorkspaces = PolicyUtils.getActiveAdminWorkspaces(allPolicies, currentUserLogin); const primaryPolicy: Policy | null | undefined = activeAdminWorkspaces.find((policy) => policy.id === activePolicyID); return primaryPolicy ?? activeAdminWorkspaces[0]; } @@ -224,11 +224,11 @@ function hasInvoicingDetails(policy: OnyxEntry): boolean { /** * Returns a primary invoice workspace for the user */ -function getInvoicePrimaryWorkspace(activePolicyID?: OnyxEntry): Policy | undefined { +function getInvoicePrimaryWorkspace(activePolicyID: OnyxEntry, currentUserLogin: string | undefined): Policy | undefined { if (PolicyUtils.canSendInvoiceFromWorkspace(activePolicyID)) { return allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID ?? '-1'}`]; } - const activeAdminWorkspaces = PolicyUtils.getActiveAdminWorkspaces(allPolicies); + const activeAdminWorkspaces = PolicyUtils.getActiveAdminWorkspaces(allPolicies, currentUserLogin); return activeAdminWorkspaces.find((policy) => PolicyUtils.canSendInvoiceFromWorkspace(policy.id)); } diff --git a/src/pages/WorkspaceSwitcherPage/index.tsx b/src/pages/WorkspaceSwitcherPage/index.tsx index 562f11aa5bc5..6dc5ecce075b 100644 --- a/src/pages/WorkspaceSwitcherPage/index.tsx +++ b/src/pages/WorkspaceSwitcherPage/index.tsx @@ -46,6 +46,7 @@ function WorkspaceSwitcherPage() { const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const [reportActions] = useOnyx(ONYXKEYS.COLLECTION.REPORT_ACTIONS); const [policies, fetchStatus] = useOnyx(ONYXKEYS.COLLECTION.POLICY); + const [currentUserLogin] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.email}); const brickRoadsForPolicies = useMemo(() => getWorkspacesBrickRoads(reports, policies, reportActions), [reports, policies, reportActions]); const unreadStatusesForPolicies = useMemo(() => getWorkspacesUnreadStatuses(reports), [reports]); @@ -104,7 +105,7 @@ function WorkspaceSwitcherPage() { } return Object.values(policies) - .filter((policy) => PolicyUtils.shouldShowPolicy(policy, !!isOffline) && !policy?.isJoinRequestPending) + .filter((policy) => PolicyUtils.shouldShowPolicy(policy, !!isOffline, currentUserLogin) && !policy?.isJoinRequestPending) .map((policy) => ({ text: policy?.name ?? '', policyID: policy?.id ?? '-1', @@ -123,7 +124,7 @@ function WorkspaceSwitcherPage() { isPolicyAdmin: PolicyUtils.isPolicyAdmin(policy), isSelected: activeWorkspaceID === policy?.id, })); - }, [policies, isOffline, getIndicatorTypeForPolicy, hasUnreadData, activeWorkspaceID]); + }, [policies, isOffline, currentUserLogin, getIndicatorTypeForPolicy, hasUnreadData, activeWorkspaceID]); const filteredAndSortedUserWorkspaces = useMemo( () => diff --git a/src/pages/home/report/SystemChatReportFooterMessage.tsx b/src/pages/home/report/SystemChatReportFooterMessage.tsx index 1c1ff2120674..4775b7ff6487 100644 --- a/src/pages/home/report/SystemChatReportFooterMessage.tsx +++ b/src/pages/home/report/SystemChatReportFooterMessage.tsx @@ -32,14 +32,17 @@ type SystemChatReportFooterMessageProps = SystemChatReportFooterMessageOnyxProps function SystemChatReportFooterMessage({choice, policies, activePolicyID}: SystemChatReportFooterMessageProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); + const [currentUserLogin] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.email}); const adminChatReportID = useMemo(() => { const adminPolicy = activePolicyID ? PolicyUtils.getPolicy(activePolicyID) - : Object.values(policies ?? {}).find((policy) => PolicyUtils.shouldShowPolicy(policy, false) && policy?.role === CONST.POLICY.ROLE.ADMIN && policy?.chatReportIDAdmins); + : Object.values(policies ?? {}).find( + (policy) => PolicyUtils.shouldShowPolicy(policy, false, currentUserLogin) && policy?.role === CONST.POLICY.ROLE.ADMIN && policy?.chatReportIDAdmins, + ); return String(adminPolicy?.chatReportIDAdmins ?? -1); - }, [activePolicyID, policies]); + }, [activePolicyID, policies, currentUserLogin]); const [adminChatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${adminChatReportID}`); diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx index b221a5d5be9d..c7f4bbb96681 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx @@ -187,7 +187,7 @@ function FloatingActionButtonAndPopover( const {isOffline} = useNetwork(); const {canUseSpotnanaTravel} = usePermissions(); - const canSendInvoice = useMemo(() => PolicyUtils.canSendInvoice(allPolicies as OnyxCollection), [allPolicies]); + const canSendInvoice = useMemo(() => PolicyUtils.canSendInvoice(allPolicies as OnyxCollection, session?.email), [allPolicies, session?.email]); const quickActionAvatars = useMemo(() => { if (quickActionReport) { diff --git a/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx b/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx index 3d2f85ef6c62..6c7769751c84 100644 --- a/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx +++ b/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx @@ -68,6 +68,7 @@ function MoneyRequestParticipantsSelector({participants = CONST.EMPTY_ARRAY, onF const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID); const policy = usePolicy(activePolicyID); const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false}); + const [currentUserLogin] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.email}); const {options, areOptionsInitialized} = useOptionsList({ shouldInitialize: didScreenTransitionEnd, }); @@ -255,7 +256,7 @@ function MoneyRequestParticipantsSelector({participants = CONST.EMPTY_ARRAY, onF if (iouType === CONST.IOU.TYPE.INVOICE) { // TODO: Use getInvoicePrimaryWorkspace when the invoices screen is ready - https://github.com/Expensify/App/issues/45175. - const policyID = option.item && ReportUtils.isInvoiceRoom(option.item) ? option.policyID : Policy.getPrimaryPolicy(activePolicyID)?.id; + const policyID = option.item && ReportUtils.isInvoiceRoom(option.item) ? option.policyID : Policy.getPrimaryPolicy(activePolicyID, currentUserLogin)?.id; newParticipants.push({ policyID, isSender: true, @@ -268,7 +269,7 @@ function MoneyRequestParticipantsSelector({participants = CONST.EMPTY_ARRAY, onF onFinish(); }, // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps -- we don't want to trigger this callback when iouType changes - [onFinish, onParticipantsAdded], + [onFinish, onParticipantsAdded, currentUserLogin], ); /** diff --git a/src/pages/iou/request/step/IOURequestStepSendFrom.tsx b/src/pages/iou/request/step/IOURequestStepSendFrom.tsx index 7064e279ee06..d0c3b28ba6da 100644 --- a/src/pages/iou/request/step/IOURequestStepSendFrom.tsx +++ b/src/pages/iou/request/step/IOURequestStepSendFrom.tsx @@ -1,5 +1,5 @@ import React, {useMemo} from 'react'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx, withOnyx} from 'react-native-onyx'; import type {OnyxCollection} from 'react-native-onyx'; import * as Expensicons from '@components/Icon/Expensicons'; import SelectionList from '@components/SelectionList'; @@ -37,11 +37,12 @@ type IOURequestStepSendFromProps = IOURequestStepSendFromOnyxProps & function IOURequestStepSendFrom({route, transaction, allPolicies}: IOURequestStepSendFromProps) { const {translate} = useLocalize(); const {transactionID, backTo} = route.params; + const [currentUserLogin] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.email}); const selectedWorkspace = useMemo(() => transaction?.participants?.find((participant) => participant.isSender), [transaction]); const workspaceOptions: WorkspaceListItem[] = useMemo(() => { - const availableWorkspaces = PolicyUtils.getActiveAdminWorkspaces(allPolicies); + const availableWorkspaces = PolicyUtils.getActiveAdminWorkspaces(allPolicies, currentUserLogin); // TODO: Uncomment the following line when the invoices screen is ready - https://github.com/Expensify/App/issues/45175. // .filter((policy) => PolicyUtils.canSendInvoiceFromWorkspace(policy.id)); diff --git a/src/pages/workspace/AccessOrNotFoundWrapper.tsx b/src/pages/workspace/AccessOrNotFoundWrapper.tsx index 816d2b74dbc6..1a6f59830506 100644 --- a/src/pages/workspace/AccessOrNotFoundWrapper.tsx +++ b/src/pages/workspace/AccessOrNotFoundWrapper.tsx @@ -36,7 +36,7 @@ const ACCESS_VARIANTS = { 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)), + (iouType !== CONST.IOU.TYPE.INVOICE || PolicyUtils.canSendInvoice(allPolicies, login)), } as const satisfies Record< string, (policy: OnyxTypes.Policy, login: string, report: OnyxTypes.Report, allPolicies: NonNullable> | null, iouType?: IOUType) => boolean diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index 94fb454c4a2d..65afba270ddf 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -46,7 +46,7 @@ import WorkspacesListRow from './WorkspacesListRow'; type WorkspaceItem = Required> & Pick & Pick & - Pick & { + Pick & { icon: AvatarSource; action: () => void; dismissError: () => void; @@ -151,7 +151,7 @@ function WorkspacesListPage({policies, reimbursementAccount, reports, session}: */ const getMenuItem = useCallback( ({item, index}: GetMenuItem) => { - const isAdmin = item.role === CONST.POLICY.ROLE.ADMIN; + const isAdmin = PolicyUtils.isPolicyAdmin(item as unknown as PolicyType, session?.email); const isOwner = item.ownerAccountID === session?.accountID; // Menu options to navigate to the chat report of #admins and #announce room. // For navigation, the chat report ids may be unavailable due to the missing chat reports in Onyx. @@ -231,7 +231,7 @@ function WorkspacesListPage({policies, reimbursementAccount, reports, session}: ); }, - [isLessThanMediumScreen, styles.mb3, styles.mh5, styles.ph5, styles.hoveredComponentBG, translate, styles.offlineFeedback.deleted, session?.accountID], + [isLessThanMediumScreen, styles.mb3, styles.mh5, styles.ph5, styles.hoveredComponentBG, translate, styles.offlineFeedback.deleted, session?.accountID, session?.email], ); const listHeaderComponent = useCallback(() => { @@ -312,7 +312,7 @@ function WorkspacesListPage({policies, reimbursementAccount, reports, session}: } return Object.values(policies) - .filter((policy): policy is PolicyType => PolicyUtils.shouldShowPolicy(policy, !!isOffline)) + .filter((policy): policy is PolicyType => PolicyUtils.shouldShowPolicy(policy, !!isOffline, session?.email)) .map((policy): WorkspaceItem => { if (policy?.isJoinRequestPending && policy?.policyDetailsForNonMembers) { const policyInfo = Object.values(policy.policyDetailsForNonMembers)[0]; @@ -352,10 +352,11 @@ function WorkspacesListPage({policies, reimbursementAccount, reports, session}: ownerAccountID: policy.ownerAccountID, role: policy.role, type: policy.type, + employeeList: policy.employeeList, }; }) .sort((a, b) => localeCompare(a.title, b.title)); - }, [reimbursementAccount?.errors, policies, isOffline, theme.textLight, policyRooms]); + }, [reimbursementAccount?.errors, policies, isOffline, theme.textLight, policyRooms, session?.email]); const getHeaderButton = () => (