|
1 | 1 | import {activePolicySelector} from '@selectors/Policy'; |
2 | | -import type {OnyxEntry} from 'react-native-onyx'; |
| 2 | +import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; |
3 | 3 | import {useSession} from '@components/OnyxListItemProvider'; |
4 | 4 | import {canSubmitPerDiemExpenseFromWorkspace, isPaidGroupPolicy, isPolicyMemberWithoutPendingDelete, isTimeTrackingEnabled} from '@libs/PolicyUtils'; |
5 | 5 | import CONST from '@src/CONST'; |
@@ -31,48 +31,85 @@ function isPolicyValidForMovingExpenses(policy: OnyxEntry<Policy>, login: string |
31 | 31 | ); |
32 | 32 | } |
33 | 33 |
|
34 | | -function usePolicyForMovingExpenses(isPerDiemRequest?: boolean, isTimeRequest?: boolean, expensePolicyID?: string) { |
35 | | - const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); |
36 | | - const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID); |
37 | | - const [activePolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID}`, { |
38 | | - selector: activePolicySelector, |
39 | | - }); |
| 34 | +type PolicyQualificationResult = { |
| 35 | + singlePolicyID: string | undefined; |
| 36 | + isMemberOfMoreThanOnePolicy: boolean; |
| 37 | + validExpensePolicyID: string | undefined; |
| 38 | +}; |
40 | 39 |
|
41 | | - const session = useSession(); |
42 | | - const login = session?.email ?? ''; |
| 40 | +/** |
| 41 | + * Selector that computes which policies qualify for moving expenses. |
| 42 | + * Returns only IDs and flags — stable output that prevents re-renders when unrelated policies change. |
| 43 | + */ |
| 44 | +function getPolicyQualificationResult( |
| 45 | + policies: OnyxCollection<Policy>, |
| 46 | + login: string, |
| 47 | + isPerDiemRequest?: boolean, |
| 48 | + isTimeRequest?: boolean, |
| 49 | + expensePolicyID?: string, |
| 50 | +): PolicyQualificationResult { |
| 51 | + if (!policies) { |
| 52 | + return {singlePolicyID: undefined, isMemberOfMoreThanOnePolicy: false, validExpensePolicyID: undefined}; |
| 53 | + } |
43 | 54 |
|
44 | | - // Early exit optimization: only need to check if we have 0, 1, or >1 policies |
45 | | - let singleUserPolicy; |
| 55 | + let singlePolicyID: string | undefined; |
46 | 56 | let isMemberOfMoreThanOnePolicy = false; |
47 | | - for (const policy of Object.values(allPolicies ?? {})) { |
| 57 | + for (const policy of Object.values(policies)) { |
48 | 58 | if (!isPolicyValidForMovingExpenses(policy, login, isPerDiemRequest, isTimeRequest)) { |
49 | 59 | continue; |
50 | 60 | } |
51 | | - |
52 | | - if (!singleUserPolicy) { |
53 | | - singleUserPolicy = policy; |
| 61 | + if (!singlePolicyID) { |
| 62 | + singlePolicyID = policy?.id; |
54 | 63 | } else { |
55 | 64 | isMemberOfMoreThanOnePolicy = true; |
56 | | - break; // Found 2, no need to continue |
| 65 | + break; |
57 | 66 | } |
58 | 67 | } |
59 | 68 |
|
60 | | - // If an expense policy ID is provided and valid, prefer it over the active policy |
61 | | - // This ensures that when viewing/editing an expense from workspace B, we show workspace B |
62 | | - // even if the user's default workspace is A |
| 69 | + let validExpensePolicyID: string | undefined; |
63 | 70 | if (expensePolicyID) { |
64 | | - const expensePolicy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${expensePolicyID}`]; |
| 71 | + const expensePolicy = policies[`${ONYXKEYS.COLLECTION.POLICY}${expensePolicyID}`]; |
65 | 72 | if (expensePolicy && isPolicyValidForMovingExpenses(expensePolicy, login, isPerDiemRequest, isTimeRequest)) { |
66 | | - return {policyForMovingExpensesID: expensePolicyID, policyForMovingExpenses: expensePolicy, shouldSelectPolicy: false}; |
| 73 | + validExpensePolicyID = expensePolicyID; |
67 | 74 | } |
68 | 75 | } |
69 | 76 |
|
| 77 | + return {singlePolicyID, isMemberOfMoreThanOnePolicy, validExpensePolicyID}; |
| 78 | +} |
| 79 | + |
| 80 | +function usePolicyForMovingExpenses(isPerDiemRequest?: boolean, isTimeRequest?: boolean, expensePolicyID?: string) { |
| 81 | + const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID); |
| 82 | + const [activePolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID}`, { |
| 83 | + selector: activePolicySelector, |
| 84 | + }); |
| 85 | + |
| 86 | + const session = useSession(); |
| 87 | + const login = session?.email ?? ''; |
| 88 | + |
| 89 | + // Contextual selector — captures login/flags from closure. |
| 90 | + // Returns only IDs + flags (stable output) to prevent re-renders when unrelated policies change. |
| 91 | + const policyQualificationSelector = (policies: OnyxCollection<Policy>) => getPolicyQualificationResult(policies, login, isPerDiemRequest, isTimeRequest, expensePolicyID); |
| 92 | + const [qualificationResult] = useOnyx(ONYXKEYS.COLLECTION.POLICY, { |
| 93 | + selector: policyQualificationSelector, |
| 94 | + }); |
| 95 | + |
| 96 | + const {singlePolicyID, isMemberOfMoreThanOnePolicy, validExpensePolicyID} = qualificationResult ?? {}; |
| 97 | + |
| 98 | + // Per-key lookup for the resolved policy (only fires when that specific policy changes) |
| 99 | + const resolvedPolicyID = validExpensePolicyID ?? singlePolicyID; |
| 100 | + const [resolvedPolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${resolvedPolicyID}`); |
| 101 | + |
| 102 | + // If an expense policy ID is provided and valid, prefer it over the active policy |
| 103 | + if (validExpensePolicyID) { |
| 104 | + return {policyForMovingExpensesID: validExpensePolicyID, policyForMovingExpenses: resolvedPolicy, shouldSelectPolicy: false}; |
| 105 | + } |
| 106 | + |
70 | 107 | if (activePolicy && (!isPerDiemRequest || canSubmitPerDiemExpenseFromWorkspace(activePolicy)) && (!isTimeRequest || isTimeTrackingEnabled(activePolicy))) { |
71 | 108 | return {policyForMovingExpensesID: activePolicyID, policyForMovingExpenses: activePolicy, shouldSelectPolicy: false}; |
72 | 109 | } |
73 | 110 |
|
74 | | - if (singleUserPolicy && !isMemberOfMoreThanOnePolicy) { |
75 | | - return {policyForMovingExpensesID: singleUserPolicy.id, policyForMovingExpenses: singleUserPolicy, shouldSelectPolicy: false}; |
| 111 | + if (singlePolicyID && !isMemberOfMoreThanOnePolicy) { |
| 112 | + return {policyForMovingExpensesID: singlePolicyID, policyForMovingExpenses: resolvedPolicy, shouldSelectPolicy: false}; |
76 | 113 | } |
77 | 114 |
|
78 | 115 | if (isMemberOfMoreThanOnePolicy) { |
|
0 commit comments