Feature/offline pattern in the report table view#62093
Conversation
|
@shawnborton can you please take a look @ videos for web and ios native and let me know if it is ok? |
|
For the failing requests video, I think we decided that we also want to show the RBR errors in the table view. cc @trjExpensify @Expensify/design for extra eyes here too. |
|
does this look ok ? Screen.Recording.2025-05-19.at.16.17.54.mov |
|
Nice, that feels great to me! |
Kicu
left a comment
There was a problem hiding this comment.
Looks good, but I left some comments that I think can improve the code readability.
Good job writing tests 👍
| const extractErrorMessages = (errors: Errors | ReceiptErrors | undefined, errorActions: ReportAction[] | undefined, translate: LocaleContextProps['translate']): string[] => { | ||
| const uniqueMessages = new Set<string>(); | ||
|
|
||
| const addErrorMessages = (rawErrors: unknown) => { |
There was a problem hiding this comment.
do we have to create a function inside a function? is it really that helpful
if you need to run a for of loop over some collection of errorActions, then we could probably "smash" the errors together like so:
const errorsToProcess = [...errors, errorActions]
^ that is probably incorrect, but something similar.
Then run the logic of addErrorMessages just once. Is something like that possible?
There was a problem hiding this comment.
refactored it a bit to avoid nested functions, but TBH not sure which one is more readable.
There was a problem hiding this comment.
Yeah I can see it still quite hard to understand what types of errors happen there.
I don't have strong opinions so if you prefer the previous approach, then return it.
|
@shawnborton @allgandalf One of you needs to copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button] |
|
@jmusial can you fix the failing test please |
|
hey @allgandalf it's the eslint on changed files, but the fail is not connected to any of my changes. The function is used in 10 places and TBH I don't think this should be addressed in this PR |
|
@allgandalf no worries 😄 Great timing though, I was just doing that! |
| const formattedCompanySpendAmount = convertToDisplayString(nonReimbursableSpend, report?.currency); | ||
| const shouldShowBreakdown = !!nonReimbursableSpend && !!reimbursableSpend; | ||
|
|
||
| const pendingChangesOpacity = useMemo(() => { |
There was a problem hiding this comment.
| const pendingChangesOpacity = useMemo(() => { | |
| const pendingActionsOpacity = useMemo(() => { |
There was a problem hiding this comment.
Actions is what we call them in our App, so lets follow the same here
| <View style={[styles.dFlex, styles.flexRow, styles.alignItemsCenter, styles.pr3]}> | ||
| <Text style={[styles.mr3, styles.textLabelSupporting]}>{translate('common.total')}</Text> | ||
| <Text style={[shouldUseNarrowLayout ? styles.mnw64p : styles.mnw100p, styles.textAlignRight, styles.textBold]}> | ||
| <Text style={[shouldUseNarrowLayout ? styles.mnw64p : styles.mnw100p, styles.textAlignRight, styles.textBold, pendingChangesOpacity]}> |
There was a problem hiding this comment.
| <Text style={[shouldUseNarrowLayout ? styles.mnw64p : styles.mnw100p, styles.textAlignRight, styles.textBold, pendingChangesOpacity]}> | |
| <Text style={[shouldUseNarrowLayout ? styles.mnw64p : styles.mnw100p, styles.textAlignRight, styles.textBold, pendingActionsOpacity]}> |
| type TransactionItemRowRBRProps = { | ||
| transaction: Transaction; | ||
| containerStyles?: ViewStyle[]; | ||
| }; |
There was a problem hiding this comment.
Please add comment at the top of each prop like we did here:
| return transaction.pendingAction; | ||
| } | ||
| const hasPendingFields = Object.keys(transaction?.pendingFields ?? {}).length > 0; | ||
| return hasPendingFields ? CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE : null; |
There was a problem hiding this comment.
@jmusial can't the pending action be add and delete? why do we always assume the action is update ?
There was a problem hiding this comment.
@allgandalf AFAIK from the RHP you can only edit fields, cannot add or delete them. You can add them from workspace settings, but I don't think this we need to adjust for this scenario here.
Also, even if you could delete a field from a transaction, from the transaction perspective that's an update.
Reviewer Checklist
Screenshots/VideosAndroid: HybridAppScreen.Recording.2025-05-29.at.12.36.54.AM.movAndroid: mWeb ChromeScreen.Recording.2025-05-29.at.12.39.54.AM.moviOS: HybridAppbuild erroriOS: mWeb SafariScreen.Recording.2025-05-29.at.12.35.33.AM.movMacOS: Chrome / SafariScreen.Recording.2025-05-29.at.12.32.10.AM.movMacOS: DesktopScreen.Recording.2025-05-29.at.12.37.43.AM.mov |
allgandalf
left a comment
There was a problem hiding this comment.
code LGTM, finishing the checklist now
|
Screen.Recording.2025-05-27.at.10.57.37.PM.movI think we should block clicking on the transaction row when we delete it offline, @shawnborton @luacmartins is that right? |
|
For example take a look at the behaviour when we delete an expense: Screen.Recording.2025-05-27.at.11.00.33.PM.mov |
|
Hey @allgandalf thanks for the find! Blocking deleted transactions' interactions makes sense, added it to the pr. Screen.Recording.2025-05-28.at.10.28.40.mov |
allgandalf
left a comment
There was a problem hiding this comment.
LGTM!!! Lets ignore the failing test! @luacmartins all. yours
mountiny
left a comment
There was a problem hiding this comment.
Changes look good to me, thank you!
| genericCreateInvoiceFailureMessage: 'Error inesperado al enviar la factura. Por favor, inténtalo de nuevo más tarde.', | ||
| receiptDeleteFailureError: 'Error inesperado al borrar este recibo. Por favor, vuelve a intentarlo más tarde.', | ||
| receiptFailureMessage: 'Hubo un error al cargar tu recibo. Por favor, ', | ||
| receiptFailureMessageShort: 'Hubo un error al cargar tu recibo.', |
There was a problem hiding this comment.
Did you get this verified?
There was a problem hiding this comment.
It's the first sentence from the message above. TBH I assumed it's correct.
|
@mountiny looks like this was merged without a test passing. Please add a note explaining why this was done and remove the |
|
Optional eslint is failing so due to the size of the pr not going to ask for it to get resolved here |
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
🚀 Deployed to staging by https://github.com/mountiny in version: 9.1.54-0 🚀
|
|
🚀 Deployed to production by https://github.com/roryabraham in version: 9.1.54-7 🚀
|
| @@ -29,6 +29,7 @@ import {navigationRef} from '@libs/Navigation/Navigation'; | |||
| import {getIOUActionForTransactionID} from '@libs/ReportActionsUtils'; | |||
There was a problem hiding this comment.
We missed a bunch of edge cases with this PR like selection of deleted expenses in offline mode, this caused #63083, be little more careful reviewing @allgandalf 😿
| const RBRMessages = [ | ||
| ...getErrorMessages( | ||
| transaction?.errors, | ||
| transactionThreadActions?.filter((e) => !!e.errors), | ||
| ), | ||
| // Some violations end with a period already so lets make sure the connected messages have only single period between them | ||
| // and end with a single dot. | ||
| ...transactionViolations.map((violation) => { |
There was a problem hiding this comment.
This array was missing FE-based required errors for fields like Merchant, leading to this issue:
which we addressed by including these FE-based required errors.

Explanation of Change
We want to add offline pattern for new expenses in the report table view
Fixed Issues
$ #61848
PROPOSAL:
N/A
Tests
Offline
1. Adding expense
2. Editing expense
3. Deleting expense
Errors
4. Editing errors
1. Log in to the app 3. Force failing requests mode 4. Go to Expense Report view 5. Open expense edit (you need to have data locally before) 6. Try to: add receipt, add comment, edit 7. RBR shows on edit view and on the list 8. When dismissed it disappearsOffline tests
Same as tests
QA Steps
Same as tests
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectioncanBeMissingparam foruseOnyxtoggleReportand notonIconClick)src/languages/*files and using the translation methodSTYLE.md) were followedAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.ScrollViewcomponent to make it scrollable when more elements are added to the page.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
#### Offlineandroid_native_offline.mov
Errors
android_native_errors.mov
Android: mWeb Chrome
android_web_offline.mov
iOS: Native
Offline
ios_native_offline.mp4
Errors
ios_native_errors.mp4
iOS: mWeb Safari
Offline
ios_web_offline.mp4
Errors
ios_web_errors.mp4
MacOS: Chrome / Safari
Offline
web_offline.mov
Errors
web_errors.mov
MacOS: Desktop