Fix: Unassigning company card offline removes it optimistically instead of making it strike through#79314
Conversation
…ad of making it strike through
Codecov Report❌ Looks like you've decreased code coverage for some files. Please write tests to increase, or at least maintain, the existing level of code coverage. See our documentation here for how to interpret this table.
|
|
@hungvu193 Please 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] |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ced92cf9e5
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| value: { | ||
| [cardID]: null, | ||
| [cardID]: { | ||
| ...card, | ||
| pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, | ||
| }, |
There was a problem hiding this comment.
Clear stale errors when retrying unassign
The optimistic merge for unassignWorkspaceCompanyCard spreads the existing card object without clearing errors, so if a previous unassign failed (see failureData setting errors later in the same function), a retry will keep the old error banner visible while the new delete is pending. This makes the UI look like the retry is still failing and can interfere with OfflineWithFeedback’s delete state. Consider explicitly setting errors: null (and any error fields) in the optimistic update when starting a new unassign.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
After checking, I think this change make sense. I could not test this in offline, but in online mode, I think we need to remove the errors optimistically
This is the current behavior. The error dimissed only after the backend call success
1.mp4
This is the behavior when we clear the errors optimistically
2.mp4
What do you think? @hungvu193
There was a problem hiding this comment.
This is the behavior when we clear the errors optimistically
I think this one is better, wdyt?
There was a problem hiding this comment.
Agreed, I'll update the PR
trjExpensify
left a comment
There was a problem hiding this comment.
Makes sense that we use pattern B and display strikethrough for pendingDelete. I didn't see it in the vids or tests, but if it fails, is the correct handling being applied? I.e reverts to 100% opacity and an in-line dismissible error message with an RBR path that leads to it?
|
@ryntgh Any updates here? |
|
Sorry for the delay, I'll update this today |
|
Resolved the conflict and updated the PR!
Yes, it uses the same failure handling as before |
| const globalCard = cardList?.[cardID]; | ||
| const isCardBeingUnassigned = globalCard?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; | ||
| const card = feedScopedCard ?? (isCardBeingUnassigned ? globalCard : undefined); | ||
| const isCardPendingDelete = card?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; |
There was a problem hiding this comment.
We recently added:
const isCardBeingUnassigned = globalCard?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE;
const card = feedScopedCard ?? (isCardBeingUnassigned ? globalCard : undefined);Can you check if we still need this logic here?
There was a problem hiding this comment.
Oh, I didn't notice this. I don't think we still need isCardPendingDelete in this case and can use isCardBeingUnassigned instead
I've re-tested and updated the PR!
There was a problem hiding this comment.
Thanks. Let me verify these changes
Reviewer Checklist
Screenshots/VideosAndroid: HybridAppScreen.Recording.2026-01-12.at.14.17.21.movAndroid: mWeb ChromeScreen.Recording.2026-01-12.at.14.13.50.moviOS: HybridAppScreen.Recording.2026-01-12.at.14.07.04.moviOS: mWeb SafariScreen.Recording.2026-01-12.at.14.04.11.movMacOS: Chrome / SafariScreen.Recording.2026-01-12.at.13.39.09.mov |
|
All yours @trjExpensify |
|
🚀 Deployed to staging by https://github.com/carlosmiceli in version: 9.3.5-0 🚀
|
|
🚀 Deployed to production by https://github.com/Beamanator in version: 9.3.5-7 🚀
|

Explanation of Change
This PR sets
pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETEin theunassignWorkspaceCompanyCardfunction instead ofnullin the optimistic data, and adds a skeleton loader while a card is being unassigned, before the card row is displayedFixed Issues
$ #78575
PROPOSAL: #78575 (comment)
Tests
Same as QA Steps
Offline tests
Same as QA Steps
QA Steps
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))npm run compress-svg)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
Android-Native.mp4
Android: mWeb Chrome
Android-mWeb.mp4
iOS: Native
iOS-Native.mp4
iOS: mWeb Safari
iOS-mWeb.mp4
MacOS: Chrome / Safari
MacOS-Chrome.mp4