-
Notifications
You must be signed in to change notification settings - Fork 3.7k
[Duplicate Expense] Allow bulk duplication. #84657
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 38 commits
Commits
Show all changes
55 commits
Select commit
Hold shift + click to select a range
5c80f02
[Duplicate Expense] Allow bulk duplication.
Krishna2323 0a910f3
add tests.
Krishna2323 17b8a60
refactor code.
Krishna2323 cff4f62
Merge branch 'main' into krishna2323/issue/84281
Krishna2323 e2b5c55
minor updates.
Krishna2323 2dae542
fix optimistic action issue.
Krishna2323 5760d55
Merge branch 'main' into krishna2323/issue/84281
Krishna2323 9591889
fix tests and ESLint.
Krishna2323 ab91ed8
fix tests.
Krishna2323 c0b25f5
add more tests.
Krishna2323 2de7466
add missing translations.
Krishna2323 55447e2
Merge branch 'main' into krishna2323/issue/84281
Krishna2323 0bb2736
fix prettier.
Krishna2323 3b6a21e
Add submitter and Per Diem date filters to bulk duplicate option
Krishna2323 dca96c3
Merge branch 'Expensify:main' into krishna2323/issue/84281
Krishna2323 fe9b4e0
address codex review comments.
Krishna2323 d2c9894
update tests.
Krishna2323 c2150c8
perf: defer bulk-duplicate Onyx subscriptions until the option is vis…
Krishna2323 d2802aa
Merge branch 'main' into krishna2323/issue/84281
Krishna2323 d6e1f5d
perf: hoist searchReports computation outside the .every() loop.
Krishna2323 9036e5c
Merge branch 'Expensify:main' into krishna2323/issue/84281
Krishna2323 370c6e3
fix: resolve source reports from search data instead of only Onyx.
Krishna2323 556354f
fix: carry forward iouReportID across bulk-duplicate loop iterations.
Krishna2323 f59ca89
Merge branch 'Expensify:main' into krishna2323/issue/84281
Krishna2323 7f485d8
Merge branch 'main' into krishna2323/issue/84281
Krishna2323 01cc84f
fix ESLint.
Krishna2323 e3ec590
fix: address review comments — plural text, displayName, production gate
Krishna2323 1f1e7b3
update translations.
Krishna2323 b96226c
fix translations.
Krishna2323 ecd002e
Merge branch 'main' into krishna2323/issue/84281
Krishna2323 7546137
add bulk duplicate expense option on report details page.
Krishna2323 e4a450b
clear selection on duplicate.
Krishna2323 6e10ed9
Merge branch 'Expensify:main' into krishna2323/issue/84281
Krishna2323 7cd5378
Merge branch 'main' into krishna2323/issue/84281
Krishna2323 db0da62
fix prettier and lint.
Krishna2323 efa3ce3
Remove manual useMemo/useCallback from React Compiler-compatible hooks
Krishna2323 d003789
Reuse BulkDuplicateHandler on report details page to avoid unconditio…
Krishna2323 255d93d
Fix duplicate handler not firing from MoneyReportHeader and remove de…
Krishna2323 aaed1b8
Merge branch 'main' into krishna2323/issue/84281
Krishna2323 a124b9c
fix TS.
Krishna2323 5bbe3b6
Memoize duplicate-related computations in useSelectedTransactionsActions
Krishna2323 b84f459
fix TS.
Krishna2323 6551b4d
Merge branch 'main' into krishna2323/issue/84281
Krishna2323 77cb659
Merge branch 'main' into krishna2323/issue/84281
Krishna2323 09c81ea
Suppress per-item sound and defer auto-submit in bulkDuplicateExpenses
Krishna2323 d3be3f3
Merge branch 'main' into krishna2323/issue/84281
Krishna2323 0ce86c5
Move bulk action type constants to CONST.REPORT and annotate eslint s…
Krishna2323 f40173f
Merge branch 'Expensify:main' into krishna2323/issue/84281
Krishna2323 3542312
remove duplicate dependency.
Krishna2323 0d7a532
fix tests.
Krishna2323 2d3c64e
Merge branch 'Expensify:main' into krishna2323/issue/84281
Krishna2323 3ad6703
Merge branch 'main' into krishna2323/issue/84281
Krishna2323 fe9eb48
Merge branch 'main' into krishna2323/issue/84281
Krishna2323 0a2f1b0
fix: remove stale shouldShowMoreContent block from merge conflict res…
Krishna2323 af30acd
fix: show bulk duplicate option for unreported expenses
Krishna2323 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
285 changes: 152 additions & 133 deletions
285
src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| import {useEffect} from 'react'; | ||
| import type {OnyxCollection} from 'react-native-onyx'; | ||
| import useBulkDuplicateAction from '@hooks/useBulkDuplicateAction'; | ||
| import type {Report, Transaction} from '@src/types/onyx'; | ||
|
|
||
| type BulkDuplicateHandlerProps = { | ||
| selectedTransactionsKeys: string[]; | ||
| allTransactions: OnyxCollection<Transaction>; | ||
| allReports: OnyxCollection<Report> | undefined; | ||
| searchData: Record<string, unknown> | undefined; | ||
| onHandlerReady: (handler: () => void) => void; | ||
| onAfterDuplicate?: () => void; | ||
| }; | ||
|
|
||
| /** | ||
| * Invisible component that subscribes to action-time Onyx data for bulk duplication. | ||
| * Only mounted when the duplicate option is visible, avoiding unnecessary global | ||
| * subscriptions on the search page for users who aren't duplicating. | ||
| */ | ||
| function BulkDuplicateHandler({selectedTransactionsKeys, allTransactions, allReports, searchData, onHandlerReady, onAfterDuplicate}: BulkDuplicateHandlerProps) { | ||
| const handleDuplicate = useBulkDuplicateAction({selectedTransactionsKeys, allTransactions, allReports, searchData, onAfterDuplicate}); | ||
|
|
||
| useEffect(() => { | ||
| onHandlerReady(handleDuplicate); | ||
| }, [handleDuplicate, onHandlerReady]); | ||
|
|
||
| return null; | ||
| } | ||
|
|
||
| export default BulkDuplicateHandler; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| import {hasSeenTourSelector} from '@selectors/Onboarding'; | ||
| import {validTransactionDraftsSelector} from '@selectors/TransactionDraft'; | ||
| import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; | ||
| import {useSearchActionsContext} from '@components/Search/SearchContext'; | ||
| import {bulkDuplicateExpenses} from '@libs/actions/IOU/Duplicate'; | ||
| import {getPolicyExpenseChat} from '@libs/ReportUtils'; | ||
| import CONST from '@src/CONST'; | ||
| import ONYXKEYS from '@src/ONYXKEYS'; | ||
| import type {Policy, Report, Transaction} from '@src/types/onyx'; | ||
| import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails'; | ||
| import useDefaultExpensePolicy from './useDefaultExpensePolicy'; | ||
| import useOnyx from './useOnyx'; | ||
| import usePermissions from './usePermissions'; | ||
|
|
||
| type UseBulkDuplicateActionParams = { | ||
| selectedTransactionsKeys: string[]; | ||
| allTransactions: OnyxCollection<Transaction>; | ||
| allReports: OnyxCollection<Report> | undefined; | ||
| searchData: Record<string, unknown> | undefined; | ||
| onAfterDuplicate?: () => void; | ||
| }; | ||
|
|
||
| /** | ||
| * Hook that subscribes to action-time-only Onyx data needed for bulk expense duplication. | ||
| * Designed to be called inside a component that only mounts when the duplicate option is visible, | ||
| * so these subscriptions don't exist for users who aren't actively duplicating. | ||
| */ | ||
| function useBulkDuplicateAction({selectedTransactionsKeys, allTransactions, allReports, searchData, onAfterDuplicate}: UseBulkDuplicateActionParams) { | ||
| const {accountID} = useCurrentUserPersonalDetails(); | ||
| const {clearSelectedTransactions} = useSearchActionsContext(); | ||
| const defaultExpensePolicy = useDefaultExpensePolicy(); | ||
| const {isBetaEnabled} = usePermissions(); | ||
| const isASAPSubmitBetaEnabled = isBetaEnabled(CONST.BETAS.ASAP_SUBMIT); | ||
|
|
||
| const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID); | ||
| const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); | ||
| const [quickAction] = useOnyx(ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE); | ||
| const [policyRecentlyUsedCurrencies] = useOnyx(ONYXKEYS.RECENTLY_USED_CURRENCIES); | ||
| const [isSelfTourViewed = false] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector}); | ||
| const [transactionDrafts] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {selector: validTransactionDraftsSelector}); | ||
| const draftTransactionIDs = Object.keys(transactionDrafts ?? {}); | ||
| const [betas] = useOnyx(ONYXKEYS.BETAS); | ||
| const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); | ||
| const [recentWaypoints] = useOnyx(ONYXKEYS.NVP_RECENT_WAYPOINTS); | ||
| const [targetPolicyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${defaultExpensePolicy?.id}`); | ||
| const [targetPolicyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${defaultExpensePolicy?.id}`); | ||
|
|
||
| const sourcePolicyIDMap: Record<string, string | undefined> = {}; | ||
| for (const transactionID of selectedTransactionsKeys) { | ||
| const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; | ||
| const reportID = transaction?.reportID; | ||
| if (!reportID) { | ||
| continue; | ||
| } | ||
| const report = (searchData?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] as Report | undefined) ?? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; | ||
| sourcePolicyIDMap[transactionID] = report?.policyID; | ||
| } | ||
|
|
||
| const handleDuplicate = () => { | ||
| const activePolicyExpenseChat = getPolicyExpenseChat(accountID, defaultExpensePolicy?.id); | ||
|
|
||
| bulkDuplicateExpenses({ | ||
| transactionIDs: selectedTransactionsKeys, | ||
| allTransactions: allTransactions ?? {}, | ||
| sourcePolicyIDMap, | ||
| targetPolicy: (defaultExpensePolicy ?? undefined) as OnyxEntry<Policy>, | ||
| targetPolicyCategories: targetPolicyCategories ?? {}, | ||
| targetPolicyTags: targetPolicyTags ?? {}, | ||
| targetReport: activePolicyExpenseChat, | ||
| personalDetails, | ||
| isASAPSubmitBetaEnabled, | ||
| introSelected, | ||
| activePolicyID, | ||
| quickAction, | ||
| policyRecentlyUsedCurrencies: policyRecentlyUsedCurrencies ?? [], | ||
| isSelfTourViewed, | ||
| transactionDrafts, | ||
| draftTransactionIDs, | ||
| betas, | ||
| recentWaypoints, | ||
| }); | ||
|
|
||
| if (onAfterDuplicate) { | ||
| onAfterDuplicate(); | ||
| } else { | ||
| clearSelectedTransactions(undefined, true); | ||
| } | ||
| }; | ||
|
|
||
| return handleDuplicate; | ||
| } | ||
|
|
||
| export default useBulkDuplicateAction; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
whats this for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BulkDuplicateHandleris a performance optimization. The actual duplication logic (useBulkDuplicateAction) requires 13+ Onyx subscriptions. This component is only mounted whenisDuplicateOptionVisibleis true, so those subscriptions don't exist for users who aren't actively duplicating. It rendersnull— its only job is to calluseBulkDuplicateActionand pass the resulting handler back to the parent viaonHandlerReady. This pattern matches the lazy-mount approach already used on the search page.