Skip to content

[$250] android-workflow-Unable to delete approval workflow #61986

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

Open
1 of 8 tasks
mitarachim opened this issue May 14, 2025 · 33 comments
Open
1 of 8 tasks

[$250] android-workflow-Unable to delete approval workflow #61986

mitarachim opened this issue May 14, 2025 · 33 comments
Assignees
Labels
Bug Something is broken. Auto assigns a BugZero manager. Engineering External Added to denote the issue can be worked on by a contributor Reviewing Has a PR in review Weekly KSv2

Comments

@mitarachim
Copy link

mitarachim commented May 14, 2025

If you haven’t already, check out our contributing guidelines for onboarding and email [email protected] to request to join our Slack channel!


Version Number: V9.1.45-0
Reproducible in staging?: Yes
Reproducible in production?: No
If this was caught during regression testing, add the test name, ID and link from TestRail: https://expensify.testrail.io/index.php?/tests/view/6097474
Email or phone of affected tester (no customers): Slottwo1 [email protected]
Issue reported by: Applause Internal Team
Device used: Redminote note 10s android 13
App Component: Workspace Settings

Action Performed:

  1. Launch app
  2. Go to workspace settings - members
  3. Invite 3 members
  4. Open workflow - tap on additional approver - upgrade
  5. Add additional approver ( user A) - click save
  6. Tap add approval workflow
  7. Select (user B) as expense from and send to this member & complete the flow.
  8. Open the flow - delete

Expected Result:

Add approval workflow must get deleted on tapping delete.

Actual Result:

Add approval workflow not deleted on tapping delete.

Workaround:

Unknown

Platforms:

  • Android: App
  • Android: mWeb Chrome
  • iOS: App
  • iOS: mWeb Safari
  • iOS: mWeb Chrome
  • Windows: Chrome
  • MacOS: Chrome / Safari
  • MacOS: Desktop

Screenshots/Videos

Bug6830254_1747172525761.Screenrecorder-2025-05-14-03-00-26-332.mp4

View all open jobs on GitHub

Upwork Automation - Do Not Edit
  • Upwork Job URL: https://www.upwork.com/jobs/~021922597876858234035
  • Upwork Job ID: 1922597876858234035
  • Last Price Increase: 2025-05-14
  • Automatic offers:
    • nkdengineer | Contributor | 107302995
Issue OwnerCurrent Issue Owner: @
@mitarachim mitarachim added DeployBlockerCash This issue or pull request should block deployment Daily KSv2 Bug Something is broken. Auto assigns a BugZero manager. DeployBlocker Indicates it should block deploying the API labels May 14, 2025
@github-actions github-actions bot added Engineering Hourly KSv2 and removed Daily KSv2 labels May 14, 2025
Copy link
Contributor

👋 Friendly reminder that deploy blockers are time-sensitive ⏱ issues! Check out the open `StagingDeployCash` deploy checklist to see the list of PRs included in this release, then work quickly to do one of the following:

  1. Identify the pull request that introduced this issue and revert it.
  2. Find someone who can quickly fix the issue.
  3. Fix the issue yourself.

Copy link

melvin-bot bot commented May 14, 2025

Triggered auto assignment to @techievivek (DeployBlockerCash), see https://stackoverflowteams.com/c/expensify/questions/9980/ for more details.

Copy link

melvin-bot bot commented May 14, 2025

Triggered auto assignment to @muttmuure (Bug), see https://stackoverflow.com/c/expensify/questions/14418 for more details. Please add this bug to a GH project, as outlined in the SO.

Copy link

melvin-bot bot commented May 14, 2025

💬 A slack conversation has been started in #expensify-open-source

@PiyushChandra17
Copy link

PiyushChandra17 commented May 14, 2025

🚨 Edited by proposal-police: This proposal was edited at 2025-05-14 07:26:13 UTC.

Proposal

Please re-state the problem that we are trying to solve in this issue.

android-workflow-Unable to delete approval workflow

What is the root cause of that problem?

Once we tap on the delete button, removeApprovalWorkflowCallback is triggered

This function is responsible for this issue:

const removeApprovalWorkflowCallback = useCallback(() => {
if (!initialApprovalWorkflow) {
return;
}
setIsDeleteModalVisible(false);
Navigation.dismissModal();
InteractionManager.runAfterInteractions(() => {
// Remove the approval workflow using the initial data as it could be already edited
removeApprovalWorkflow(route.params.policyID, initialApprovalWorkflow);
});
}, [initialApprovalWorkflow, route.params.policyID]);

It calls the removeApprovalWorkflow from the actions

{
onyxMethod: Onyx.METHOD.SET,
key: ONYXKEYS.APPROVAL_WORKFLOW,
value: null,
},

Once deleted it sets the value to null but it's not updated in the UI

InteractionManager.runAfterInteractions(() => {
            // Remove the approval workflow using the initial data as it could be already edited
            removeApprovalWorkflow(route.params.policyID, initialApprovalWorkflow);
        });

this block is the root cause InteractionManager.runAfterInteractions(() =>

What changes do you think we should make in order to solve the problem?

We should call removeApprovalWorkflow(route.params.policyID, initialApprovalWorkflow) function
below Navigation.dismissModal();

and remove InteractionManager.runAfterInteractions(() =>

const removeApprovalWorkflowCallback = useCallback(() => {
        if (!initialApprovalWorkflow) {
            return;
        }

        setIsDeleteModalVisible(false);
        Navigation.dismissModal();
        removeApprovalWorkflow(route.params.policyID, initialApprovalWorkflow);
        
    }, [initialApprovalWorkflow, route.params.policyID]);

What specific scenarios should we cover in automated tests to prevent reintroducing this issue in the future?

N/A

What alternative solutions did you explore? (Optional)

If deferring via InteractionManager.runAfterInteractions is delaying it and taking too long to invoke the action/Call the removeApprovalWorkflow API~ the UI never picks it up. We can use setTimeout instead

const removeApprovalWorkflowCallback = useCallback(() => {
        if (!initialApprovalWorkflow) {
            return;
        }

        setIsDeleteModalVisible(false);
        Navigation.dismissModal();
        setTimeout(() => {
            removeApprovalWorkflow(route.params.policyID, initialApprovalWorkflow);
          }, 0);
    }, [initialApprovalWorkflow, route.params.policyID]);

Result

Image

workflow_delete_approver_ios.mp4

Reminder: Please use plain English, be brief and avoid jargon. Feel free to use images, charts or pseudo-code if necessary. Do not post large multi-line diffs or write walls of text. Do not create PRs unless you have been hired for this job.

@mountiny mountiny removed the DeployBlocker Indicates it should block deploying the API label May 14, 2025
@PiyushChandra17
Copy link

@techievivek Since this is a DeployBlockerCash, i can help raise a quick PR if it goes external. Thanks

@techievivek
Copy link
Contributor

Thanks! I am just confused why this issue isn't reproducible in production, especially since the related code hasn't changed in a while.

@PiyushChandra17
Copy link

@techievivek I think this issue is reproducible on latest main, also i think it's occuring on iOS aswell. Can you please verify? Thanks

@techievivek
Copy link
Contributor

My DEV setup for iOS/Android is currently broken, so I can’t verify it there. However, I did test this on iOS version V9.1.44-8(probably latest production deploy) and wasn’t able to reproduce the issue. I also reviewed the recent deploy diff but didn’t find anything there either. This is particularly tricky since the issue doesn’t reproduce on web. Let me bring in a C+ to help us review the proposal. Thanks!

@techievivek techievivek added the External Added to denote the issue can be worked on by a contributor label May 14, 2025
@melvin-bot melvin-bot bot changed the title android-workflow-Unable to delete approval workflow [$250] android-workflow-Unable to delete approval workflow May 14, 2025
Copy link

melvin-bot bot commented May 14, 2025

Job added to Upwork: https://www.upwork.com/jobs/~021922597876858234035

@melvin-bot melvin-bot bot added the Help Wanted Apply this label when an issue is open to proposals by contributors label May 14, 2025
Copy link

melvin-bot bot commented May 14, 2025

Triggered auto assignment to Contributor-plus team member for initial proposal review - @ahmedGaber93 (External)

@nkdengineer
Copy link
Contributor

nkdengineer commented May 14, 2025

🚨 Edited by proposal-police: This proposal was edited at 2025-05-14 14:39:18 UTC.

Proposal

Please re-state the problem that we are trying to solve in this issue.

Add approval workflow not deleted on tapping delete.

What is the root cause of that problem?

This comes from #60023 as they replaced current Modal with a new one. The interaction is not cleared when the modal is closed because it's unmounted

if (handleRef.current) {
InteractionManager.clearInteractionHandle(handleRef.current);
}
if (getPlatform() !== CONST.PLATFORM.IOS) {
onModalHide();
}
}, [onModalHide]);

Then the logic inside runAfterInteraction is nevered triggered

setIsDeleteModalVisible(false);
Navigation.dismissModal();
InteractionManager.runAfterInteractions(() => {
// Remove the approval workflow using the initial data as it could be already edited
removeApprovalWorkflow(route.params.policyID, initialApprovalWorkflow);
});

What changes do you think we should make in order to solve the problem?

We should also clear the interaction in the clean-up function of this useEffect

return () => {
    if (!handleRef.current) {
        return;
    }

    InteractionManager.clearInteractionHandle(handleRef.current);
};

useEffect(() => {
if (isVisible && !isContainerOpen && !isTransitioning) {
handleRef.current = InteractionManager.createInteractionHandle();
onModalWillShow();
setIsVisibleState(true);
setIsTransitioning(true);
} else if (!isVisible && isContainerOpen && !isTransitioning) {
handleRef.current = InteractionManager.createInteractionHandle();
onModalWillHide();
setIsVisibleState(false);
setIsTransitioning(true);
}
// eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
}, [isVisible, isContainerOpen, isTransitioning]);

or we can only clear it if the component is unmounted

useEffect(() => {
    return () => {
        if (!handleRef.current) {
            return;
        }

        InteractionManager.clearInteractionHandle(handleRef.current);
    };
}, []);

if (handleRef.current) {
InteractionManager.clearInteractionHandle(handleRef.current);
}
if (getPlatform() !== CONST.PLATFORM.IOS) {
onModalHide();
}
}, [onModalHide]);

What specific scenarios should we cover in automated tests to prevent reintroducing this issue in the future?

None

What alternative solutions did you explore? (Optional)

NA

Reminder: Please use plain English, be brief and avoid jargon. Feel free to use images, charts or pseudo-code if necessary. Do not post large multi-line diffs or write walls of text. Do not create PRs unless you have been hired for this job.

@ahmedGaber93
Copy link
Contributor

ahmedGaber93 commented May 14, 2025

Thanks all for the proposals

@nkdengineer how many places are affected by this change? I think we use the same logic in other places like there in the same file

Navigation.dismissModal();
InteractionManager.runAfterInteractions(() => {
updateApprovalWorkflow(route.params.policyID, approvalWorkflow, membersToRemove, approversToRemove);
});

@linhvovan29546
Copy link
Contributor

linhvovan29546 commented May 14, 2025

@ahmedGaber93 I haven’t investigated deeply, but I believe the root cause is that BottomDockedModal clears the interaction handle, so runAfterInteractions never gets triggered in the step mentioned by the OP

if (handleRef.current) {
InteractionManager.clearInteractionHandle(handleRef.current);
}

if (handleRef.current) {
InteractionManager.clearInteractionHandle(handleRef.current);
}

Removing that line will resolve the issue, but it might introduce other problems, maybe SWM knows that

@ahmedGaber93
Copy link
Contributor

@linhvovan29546 Hmm! I comment those lines, but it not works with me. Can you test again and share a video?

@PiyushChandra17
Copy link

@linhvovan29546
Copy link
Contributor

linhvovan29546 commented May 14, 2025

I removed those lines, thinking they were the same, but they’re actually different. :) not sure that

handleRef.current = InteractionManager.createInteractionHandle();

It looks like we're calling createInteractionHandle when hide modal, but not clearing it afterward

@nkdengineer
Copy link
Contributor

@ahmedGaber93 I checked and I see the logic clear interaction inside onCloseCallBack isn't triggered then runAfterInteraction is never triggered. The reason maybe we called the dismissModal then it's unmounted.

if (handleRef.current) {
InteractionManager.clearInteractionHandle(handleRef.current);
}
if (getPlatform() !== CONST.PLATFORM.IOS) {
onModalHide();
}
}, [onModalHide]);

So we should also clear the interaction in the clean-up function of this useEffect

return () => {
    if (!handleRef.current) {
        return;
    }

    InteractionManager.clearInteractionHandle(handleRef.current);
};

useEffect(() => {
if (isVisible && !isContainerOpen && !isTransitioning) {
handleRef.current = InteractionManager.createInteractionHandle();
onModalWillShow();
setIsVisibleState(true);
setIsTransitioning(true);
} else if (!isVisible && isContainerOpen && !isTransitioning) {
handleRef.current = InteractionManager.createInteractionHandle();
onModalWillHide();
setIsVisibleState(false);
setIsTransitioning(true);
}
// eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
}, [isVisible, isContainerOpen, isTransitioning]);

or we can only clear it if the component is unmounted

useEffect(() => {
    return () => {
        if (!handleRef.current) {
            return;
        }

        InteractionManager.clearInteractionHandle(handleRef.current);
    };
}, []);

I tested both solutions and it works.

@ahmedGaber93
Copy link
Contributor

ahmedGaber93 commented May 14, 2025

Thanks, @linhvovan29546 @nkdengineer for investing this.

The only thing we still need to confirm is that case #57536 is not broken (the case that those lines add for fixing it).

@ahmedGaber93
Copy link
Contributor

@PiyushChandra17 thanks for the proposal.

Removing InteractionManager.runAfterInteractions will back the issue that mentioned here #61986 (comment), also setTimeout is a workaround that not fix the root cause.

@PiyushChandra17
Copy link

PiyushChandra17 commented May 14, 2025

@ahmedGaber93 I think on @nkdengineer proposal here close property doesn't exist. Maybe call Navigation.dismissModal() without it

@nkdengineer
Copy link
Contributor

nkdengineer commented May 14, 2025

@ahmedGaber93 The reason in FAB is the navigation logic is only triggered after the modal is closed then the interaction have already cleared on the close callback.

@ahmedGaber93
Copy link
Contributor

@nkdengineer anyway we don't remove that logic, we just add extra clearInteractionHandler to confirm that interaction is cleared.

Just update your proposal with changes here #61986 (comment) for reference.

@nkdengineer
Copy link
Contributor

anyway we don't remove that logic, we just add extra clearInteractionHandler to confirm that interaction is cleared.

What do you mean? Do you mean the logic create interaction or clear interaction?

@ahmedGaber93
Copy link
Contributor

ahmedGaber93 commented May 14, 2025

I mean, your fix have no effect on this PR fix, because we just add new clearInteractionHandler to confirm that interaction (handleRef.current) is cleared.

@ahmedGaber93
Copy link
Contributor

Just update your proposal with this #61986 (comment) to move forward.

@ahmedGaber93
Copy link
Contributor

ahmedGaber93 commented May 14, 2025

@nkdengineer's proposal works fine with me!

We have uncomplete interaction here that prevent any runAfterInteractions after it to trigger. Clearing it in this useEffect fix the issue.

🎀 👀 🎀 C+ reviewed

Copy link

melvin-bot bot commented May 14, 2025

Current assignee @techievivek is eligible for the choreEngineerContributorManagement assigner, not assigning anyone new.

@melvin-bot melvin-bot bot removed the Help Wanted Apply this label when an issue is open to proposals by contributors label May 14, 2025
Copy link

melvin-bot bot commented May 14, 2025

📣 @nkdengineer 🎉 An offer has been automatically sent to your Upwork account for the Contributor role 🎉 Thanks for contributing to the Expensify app!

Offer link
Upwork job
Please accept the offer and leave a comment on the Github issue letting us know when we can expect a PR to be ready for review 🧑‍💻
Keep in mind: Code of Conduct | Contributing 📖

@melvin-bot melvin-bot bot added Reviewing Has a PR in review Weekly KSv2 and removed Daily KSv2 labels May 14, 2025
@ahmedGaber93
Copy link
Contributor

@nkdengineer please remember this #61986 (comment)

@nkdengineer
Copy link
Contributor

@ahmedGaber93 Updated.

Copy link

melvin-bot bot commented May 14, 2025

⚠️ Looks like this issue was linked to a Deploy Blocker here

If you are the assigned CME please investigate whether the linked PR caused a regression and leave a comment with the results.

If a regression has occurred and you are the assigned CM follow the instructions here.

If this regression could have been avoided please consider also proposing a recommendation to the PR checklist so that we can avoid it in the future.

@LorenzoBloedow
Copy link
Contributor

Sorry, didn't mean to trigger melvin, not a deploy blocker, just two issues caused by the same underlying problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Something is broken. Auto assigns a BugZero manager. Engineering External Added to denote the issue can be worked on by a contributor Reviewing Has a PR in review Weekly KSv2
Projects
None yet
Development

No branches or pull requests

10 participants