-
Notifications
You must be signed in to change notification settings - Fork 4k
[No QA] [Better Expense Report View] Add TransactionPreview component #57614
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
mountiny
merged 31 commits into
Expensify:main
from
software-mansion-labs:transaction-preview
Mar 17, 2025
Merged
Changes from all commits
Commits
Show all changes
31 commits
Select commit
Hold shift + click to select a range
cc2fa63
Create TransactionPreview component & story for it
JakubKorytko 4820723
Add two variants to storybook & correct date
JakubKorytko 68ab7cb
Create TransactionPreview structure in a new dir
JakubKorytko 596b40c
Remove redundant props from TransactionPreview
JakubKorytko b68506d
Improve TransactionPreview storybook
JakubKorytko e59e825
Add every visual version of TransactionPreview
JakubKorytko 801d44c
Add dynamic assets and Figma values to story
JakubKorytko 9c8fcba
Generate RBRmessage in TransactionPreviewContent
JakubKorytko ca764ed
Correct component preview header text & story
JakubKorytko 6c7a2a3
Use existing styles for Avatars
JakubKorytko 398280b
Add IOU indicator to TransactionPreview
JakubKorytko 5230e5c
Change the structure of TransactionPreview
JakubKorytko 177c905
Remove redundant hooks
JakubKorytko 96bc336
Correct styling
JakubKorytko 8abb843
Apply fixes mentioned in Design Doc
JakubKorytko 92a6498
Remove named imports
JakubKorytko 6f434c4
Add TransactionPreviewUtils tests
JakubKorytko ee0cff0
Redesign the structure and story
JakubKorytko b505da1
Apply sumo-slonik & Kicu review comments
JakubKorytko 4719c05
Minor visual fixes
JakubKorytko c9761e3
Fix TransactionPreview styles & add hold translate path
JakubKorytko 0d08b2a
Fix Transaction test & IOU header font size
JakubKorytko 830ed3c
Add NoMerchant variant to Storybook
JakubKorytko bd3063f
Fix UI according to Figma
JakubKorytko 96eba91
Add fixes according to mountiny comments
JakubKorytko a71f8c1
Add more tests for TransactionPreviewUtils
JakubKorytko 24dc63e
Cover shouldShowSplitShare & showCashOrCard in tests
JakubKorytko 1784f8c
Merge branch 'main' into transaction-preview
JakubKorytko 697bb11
Fix TransactionPreviewUtils tests after merge
JakubKorytko b8ef110
Merge branch 'main' into transaction-preview
JakubKorytko 51430f9
Fix Jest workflow
JakubKorytko 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
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
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
388 changes: 388 additions & 0 deletions
388
src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx
Large diffs are not rendered by default.
Oops, something went wrong.
80 changes: 80 additions & 0 deletions
80
src/components/ReportActionItem/TransactionPreview/index.tsx
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,80 @@ | ||
| import {useRoute} from '@react-navigation/native'; | ||
| import React, {useCallback, useMemo} from 'react'; | ||
| import type {GestureResponderEvent} from 'react-native'; | ||
| import {useOnyx} from 'react-native-onyx'; | ||
| import {showContextMenuForReport} from '@components/ShowContextMenuContext'; | ||
| import useTransactionViolations from '@hooks/useTransactionViolations'; | ||
| import {getOriginalMessage, isMoneyRequestAction as isMoneyRequestActionReportActionsUtils} from '@libs/ReportActionsUtils'; | ||
| import {getReviewNavigationRoute} from '@libs/TransactionPreviewUtils'; | ||
| import {removeSettledAndApprovedTransactions} from '@libs/TransactionUtils'; | ||
| import Navigation from '@navigation/Navigation'; | ||
| import type {PlatformStackRouteProp} from '@navigation/PlatformStackNavigation/types'; | ||
| import type {TransactionDuplicateNavigatorParamList} from '@navigation/types'; | ||
| import {clearWalletTermsError} from '@userActions/PaymentMethods'; | ||
| import {clearIOUError} from '@userActions/Report'; | ||
| import CONST from '@src/CONST'; | ||
| import ONYXKEYS from '@src/ONYXKEYS'; | ||
| import type SCREENS from '@src/SCREENS'; | ||
| import TransactionPreviewContent from './TransactionPreviewContent'; | ||
| import type {TransactionPreviewProps} from './types'; | ||
|
|
||
| function TransactionPreview(props: TransactionPreviewProps) { | ||
| const {action, chatReportID, reportID, contextMenuAnchor, checkIfContextMenuActive = () => {}, shouldDisplayContextMenu, iouReportID} = props; | ||
|
|
||
| const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`); | ||
| const route = useRoute<PlatformStackRouteProp<TransactionDuplicateNavigatorParamList, typeof SCREENS.TRANSACTION_DUPLICATE.REVIEW>>(); | ||
| const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${route.params?.threadReportID}`); | ||
| const isMoneyRequestAction = isMoneyRequestActionReportActionsUtils(action); | ||
| const transactionID = isMoneyRequestAction ? getOriginalMessage(action)?.IOUTransactionID : null; | ||
| const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`); | ||
| const violations = useTransactionViolations(transaction?.transactionID); | ||
| const [walletTerms] = useOnyx(ONYXKEYS.WALLET_TERMS); | ||
| const [session] = useOnyx(ONYXKEYS.SESSION); | ||
| const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`); | ||
| const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); | ||
|
|
||
| // Get transaction violations for given transaction id from onyx, find duplicated transactions violations and get duplicates | ||
| const allDuplicates = useMemo(() => violations?.find((violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? [], [violations]); | ||
| const duplicates = useMemo(() => removeSettledAndApprovedTransactions(allDuplicates), [allDuplicates]); | ||
| const sessionAccountID = session?.accountID; | ||
| const areThereDuplicates = allDuplicates.length > 0 && duplicates.length > 0 && allDuplicates.length === duplicates.length; | ||
|
|
||
| const showContextMenu = (event: GestureResponderEvent) => { | ||
| if (!shouldDisplayContextMenu) { | ||
| return; | ||
| } | ||
| showContextMenuForReport(event, contextMenuAnchor, reportID, action, checkIfContextMenuActive); | ||
| }; | ||
|
|
||
| const offlineWithFeedbackOnClose = useCallback(() => { | ||
| clearWalletTermsError(); | ||
| clearIOUError(chatReportID); | ||
| }, [chatReportID]); | ||
|
|
||
| const navigateToReviewFields = useCallback(() => { | ||
| Navigation.navigate(getReviewNavigationRoute(route, report, transaction, duplicates)); | ||
| }, [duplicates, report, route, transaction]); | ||
|
|
||
| return ( | ||
| <TransactionPreviewContent | ||
| /* eslint-disable-next-line react/jsx-props-no-spreading */ | ||
| {...props} | ||
| chatReport={chatReport} | ||
| personalDetails={personalDetails} | ||
| iouReport={iouReport} | ||
| transaction={transaction} | ||
| violations={violations} | ||
| showContextMenu={showContextMenu} | ||
| offlineWithFeedbackOnClose={offlineWithFeedbackOnClose} | ||
| navigateToReviewFields={navigateToReviewFields} | ||
| areThereDuplicates={areThereDuplicates} | ||
| sessionAccountID={sessionAccountID} | ||
| walletTermsErrors={walletTerms?.errors} | ||
| routeName={route.name} | ||
| /> | ||
| ); | ||
| } | ||
|
|
||
| TransactionPreview.displayName = 'TransactionPreview'; | ||
|
|
||
| export default TransactionPreview; |
112 changes: 112 additions & 0 deletions
112
src/components/ReportActionItem/TransactionPreview/types.ts
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,112 @@ | ||
| import type {GestureResponderEvent, StyleProp, ViewStyle} from 'react-native'; | ||
| import type {OnyxEntry} from 'react-native-onyx'; | ||
| import type {ContextMenuAnchor} from '@pages/home/report/ContextMenu/ReportActionContextMenu'; | ||
| import type {PersonalDetailsList, Report, ReportAction, Transaction, TransactionViolations} from '@src/types/onyx'; | ||
| import type {Errors} from '@src/types/onyx/OnyxCommon'; | ||
|
|
||
| type TransactionPreviewProps = { | ||
| /** The active IOUReport, used for Onyx subscription */ | ||
| iouReportID: string | undefined; | ||
|
|
||
| /** The associated chatReport */ | ||
| chatReportID: string | undefined; | ||
|
|
||
| /** The ID of the current report */ | ||
| reportID: string | undefined; | ||
|
|
||
| /** Callback for the preview pressed */ | ||
| onPreviewPressed: (event?: GestureResponderEvent | KeyboardEvent) => void; | ||
|
|
||
| /** All the data of the action, used for showing context menu */ | ||
| action: ReportAction; | ||
|
|
||
| /** Popover context menu anchor, used for showing context menu */ | ||
| contextMenuAnchor?: ContextMenuAnchor; | ||
|
|
||
| /** Callback for updating context menu active state, used for showing context menu */ | ||
| checkIfContextMenuActive?: () => void; | ||
|
|
||
| /** Extra styles to pass to View wrapper */ | ||
| containerStyles?: StyleProp<ViewStyle>; | ||
|
|
||
| /** True if this IOU has a type of split */ | ||
| isBillSplit: boolean; | ||
|
|
||
| /** Whether this IOU is a track expense */ | ||
| isTrackExpense: boolean; | ||
|
|
||
| /** True if the IOU Preview card is hovered */ | ||
| isHovered?: boolean; | ||
|
|
||
| /** Whether or not an IOU report contains expenses in a different currency | ||
| * that are either created or cancelled offline, and thus haven't been converted to the report's currency yet | ||
| */ | ||
| shouldShowPendingConversionMessage?: boolean; | ||
|
|
||
| /** Whether a message is a whisper */ | ||
| isWhisper?: boolean; | ||
|
|
||
| /** Whether context menu should be shown on press */ | ||
| shouldDisplayContextMenu?: boolean; | ||
| }; | ||
|
|
||
| type TransactionPreviewContentProps = { | ||
| /** Function to display the context menu in response to an event. */ | ||
| showContextMenu: (event: GestureResponderEvent) => void; | ||
|
|
||
| /** Handles the UI response and data clean-up when the transaction goes offline. */ | ||
| offlineWithFeedbackOnClose: () => void; | ||
|
|
||
| /** Navigates the user to a separate view or component for reviewing or editing transaction fields. */ | ||
| navigateToReviewFields: () => void; | ||
|
|
||
| /** General callback for handling presses on the preview component, can also handle keyboard events. */ | ||
| onPreviewPressed: (event?: GestureResponderEvent | KeyboardEvent | undefined) => void; | ||
|
|
||
| /** Whether the transaction is whisper. */ | ||
| isWhisper?: boolean; | ||
|
|
||
| /** Determines if the element is currently hovered over. */ | ||
| isHovered?: boolean; | ||
|
|
||
| /** Optional custom styles to be applied to container components. */ | ||
| containerStyles?: StyleProp<ViewStyle>; | ||
|
|
||
| /** Records any errors related to wallet terms. */ | ||
| walletTermsErrors: Errors | undefined; | ||
|
|
||
| /** Represents the IOU report entry from Onyx */ | ||
| iouReport: OnyxEntry<Report>; | ||
|
|
||
| /** Flag to determine if a transaction involves a bill split among multiple parties. */ | ||
| isBillSplit: boolean; | ||
|
|
||
| /** Holds the transaction data entry from Onyx */ | ||
| transaction: OnyxEntry<Transaction>; | ||
|
|
||
| /** Represents the action entry from Onyx */ | ||
| action: ReportAction; | ||
|
|
||
| /** Contains data about potential transaction violations */ | ||
| violations: TransactionViolations; | ||
|
|
||
| /** Holds the chat report entry from Onyx */ | ||
| chatReport?: Report; | ||
|
|
||
| /** Optional details about people involved in the transaction */ | ||
| personalDetails?: PersonalDetailsList; | ||
|
|
||
| /** Indicates whether the transaction consists of duplicates */ | ||
| areThereDuplicates: boolean; | ||
|
|
||
| /** Session account ID */ | ||
| sessionAccountID?: number; | ||
|
|
||
| /** Name of the route where the transaction preview is being displayed */ | ||
| routeName: string; | ||
|
|
||
| /** Determine whether to hide the component's children if deletion is pending */ | ||
| shouldHideOnDelete?: boolean; | ||
| }; | ||
|
|
||
| export type {TransactionPreviewProps, TransactionPreviewContentProps}; |
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 |
|---|---|---|
|
|
@@ -416,13 +416,15 @@ function TransactionListItemRow({ | |
| </View> | ||
| <View style={[StyleUtils.getSearchTableColumnStyles(CONST.SEARCH.TABLE_COLUMNS.FROM)]}> | ||
| <UserInfoCell | ||
| participant={item.from} | ||
| accountID={item.from.accountID} | ||
| avatar={item.from.avatar} | ||
| displayName={item.formattedFrom} | ||
| /> | ||
| </View> | ||
| <View style={[StyleUtils.getSearchTableColumnStyles(CONST.SEARCH.TABLE_COLUMNS.FROM)]}> | ||
| <UserInfoCell | ||
| participant={item.to} | ||
| accountID={item.to.accountID} | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| avatar={item.to.avatar} | ||
| displayName={item.formattedTo} | ||
| /> | ||
| </View> | ||
|
|
||
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
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.
Since we have the tableReportView beta, could we just use that now and render the new component if user is on that beta?
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.
Maybe to keep the PR cleaner, let's not modify the app code in this PR so that it only adds to storybook. We can use them in the code later, when other PR's (like the carousel) are ready