App icon indicating copy to clipboard operation
App copied to clipboard

[$250] Hybrid - Android - WS Switcher - App is closed when switching workspace and using device back button.

Open IuliiaHerets opened this issue 1 year ago β€’ 15 comments

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: 9.0.71-0 Reproducible in staging?: Yes Reproducible in production?: No If this was caught on HybridApp, is this reproducible on New Expensify Standalone?: No, reproducible on hybrid only If this was caught during regression testing, add the test name, ID and link from TestRail: N/A Email or phone of affected tester (no customers): [email protected] Issue reported by: Applause Internal Team

Action Performed:

  1. Launch the Expensify app.
  2. Tap on the workspace switcher on the top left corner.
  3. Select any workspace.
  4. Use the device back button.
  5. Verify that now all the chats are displayed again on inbox.

Expected Result:

After switching workspaces and using device back button, all the chats should be displayed again on inbox.

Actual Result:

The app is closed instead of displaying all chats again, when the user switch workspaces and uses device back button. When the app is reopened, all chats are displayed in inbox.

Workaround:

Unknown

Platforms:

  • [ ] Android: Standalone
  • [x] Android: HybridApp
  • [ ] Android: mWeb Chrome
  • [ ] iOS: Standalone
  • [ ] iOS: HybridApp
  • [ ] iOS: mWeb Safari
  • [ ] MacOS: Chrome / Safari
  • [ ] MacOS: Desktop

Screenshots/Videos

https://github.com/user-attachments/assets/d0d7c9cf-0123-4df6-a844-d8083efbebe8

View all open jobs on GitHub

Upwork Automation - Do Not Edit
  • Upwork Job URL: https://www.upwork.com/jobs/~021864885051135538956
  • Upwork Job ID: 1864885051135538956
  • Last Price Increase: 2024-12-06
Issue OwnerCurrent Issue Owner: @eh2077

IuliiaHerets avatar Dec 04 '24 09:12 IuliiaHerets

Triggered auto assignment to @RachCHopkins (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.

melvin-bot[bot] avatar Dec 04 '24 09:12 melvin-bot[bot]

Can Repro - When I hit the device back button it closes the app entirely. I am on 9.0.71-1

RachCHopkins avatar Dec 06 '24 04:12 RachCHopkins

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

melvin-bot[bot] avatar Dec 06 '24 04:12 melvin-bot[bot]

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

melvin-bot[bot] avatar Dec 06 '24 04:12 melvin-bot[bot]

Proposal

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

When the user switches to a specific workspace chats and then uses device back button, the app is closed instead of displaying all chats again (Expensify).

What is the root cause of that problem?

[!note] The issue is present on Android: Native as well as on Android: HybridApp (more details below).

In the WorkspaceSwitcherPage we have the selectPolicy function which is called when we switch to a specific workspace chats. If we follow Navigation.navigateWithSwitchPolicyID down to source we arrive to the switchPolicyID function where at the bottom we get to the root cause of why, when we use the device back button the app will close instead of returning back to all chats (Expensify):

https://github.com/Expensify/App/blob/b595f5d24141a403fcc3f44c73d00cd6951dca7d/src/libs/Navigation/switchPolicyID.ts#L150-L156

as this block comes into action on narrow layout devices because of this condition:

https://github.com/Expensify/App/blob/b595f5d24141a403fcc3f44c73d00cd6951dca7d/src/libs/Navigation/switchPolicyID.ts#L115-L116

Because that block pops all central pane routes out in order for the bottom tab navigator to be visible, this means that the only route present in the stack is BottomTabNavigator which is the same as when we open the app -> which when pressing hardware / device back button it will close the app as expected because there's no other route to go back to.

What's the code responsible for handling the hardware / device back button and close the app on Android ?

We have this android platform specific function setupCustomAndroidBackHandler which is used to dictate the behaviour and if we look at its code, there is this block which is Android: HybridApp specific that tells the app to close when isLastScreenOnStack is true which is the case for our issue:

https://github.com/Expensify/App/blob/b595f5d24141a403fcc3f44c73d00cd6951dca7d/src/libs/Navigation/setupCustomAndroidBackHandler/index.android.ts#L27-L29

For Android: Native though we don't have any code in this function to specifically handle behaviour, therefore it defaults to the standard behaviour (close the app) at the end of the function:

https://github.com/Expensify/App/blob/b595f5d24141a403fcc3f44c73d00cd6951dca7d/src/libs/Navigation/setupCustomAndroidBackHandler/index.android.ts#L66-L67

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

To solve our issue on both Android: Native and HybridApp we have to implement the following changes:

  1. Under: https://github.com/Expensify/App/blob/b595f5d24141a403fcc3f44c73d00cd6951dca7d/src/libs/Navigation/setupCustomAndroidBackHandler/index.android.ts#L25

add the following logic:

const isWorkspaceHomeOnlyRoute = bottomTabRoutes.every((route) => route.name === SCREENS.HOME && !!(route.params as {policyID?: string})?.policyID);

if (isLastScreenOnStack && isWorkspaceHomeOnlyRoute) {
    navigationRef.dispatch({...StackActions.replace(SCREENS.HOME)});
    return true;
}

Explanation

  1. isWorkspaceHomeOnlyRoute checks whether in bottomTabRoutes (array) every Home route has the policyID param
  2. If isLastScreenOnStack and isWorkspaceHomeOnlyRoute are both true - this will always be the case when we are on the specific workspace chats home screen since there is only one route in the stack and it is the Home route with policyID param
  3. When we go back with device back button and this condition is true -> we will replace the Home screen with policyID param with the Home screen without any params, which is the Expensify home page with all chats.

This will fix the issue as required by the Expected result (see video below) because when we will perform the go back action we will have the Home screen in the stack and will be navigated to Home (all chats), then if we go back again from Home then the app will close as expected.

Additional explanation regarding Android: HybridApp -> under the new logic added above we will keep the Android: HybridApp logic unchanged since in case the newly added condition doesn't pass it means we want to keep the existing NativeModules.HybridAppModule.exitApp() logic because without this I assume the app will not close as the Android: Native app does by default.

cc @adamgrzybowski @Kicu for a take on the proposed solution as they worked on setupCustomAndroidBackHandler

Result video (before / after)

Android: Native
Before After

ikevin127 avatar Dec 08 '24 02:12 ikevin127

Proposal

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

Android - WS Switcher - App is closed when switching workspace and using device back button.

What is the root cause of that problem?

  • The issue originates from this commit in the PR: Migrate E/App to use PlatformStackNavigation.
    • Prior to this change, the problem did not exist.
    • The root cause is the use of the Replace navigation method instead of Push.
    • When switching to another workspace and pressing the back button, the app closes because the stack contains only one entry, which triggers this condition.

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

  • When switching workspaces from WorkspaceSwitcherPage, we should use the Push navigation method instead of Replace. To implement we need:
  • Update the getActionForBottomTabNavigator function
    • Add an optional prop shouldReplaceHome with a default value of true here https://github.com/Expensify/App/blob/7925c9b555af60156c62947ae63b517d6c31587f/src/libs/Navigation/switchPolicyID.ts#L29
    • And here https://github.com/Expensify/App/blob/7925c9b555af60156c62947ae63b517d6c31587f/src/libs/Navigation/switchPolicyID.ts#L76 https://github.com/Expensify/App/blob/7925c9b555af60156c62947ae63b517d6c31587f/src/libs/Navigation/switchPolicyID.ts#L102 https://github.com/Expensify/App/blob/7925c9b555af60156c62947ae63b517d6c31587f/src/libs/Navigation/switchPolicyID.ts#L29
  • Update the navigateWithSwitchPolicyID function in WorkspaceSwitcherPage:
    • Pass shouldReplaceHome: false .
    • This ensures the navigation method is Push instead of Replace when called from this screen.
    • (Optional) Add a condition to ensure this change only applies to Android.
Results ### Demo Videos
Before After

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

Because this issue come from this, we need to check other related cases.

What alternative solutions did you explore? (Optional)

N/A

linhvovan29546 avatar Dec 09 '24 01:12 linhvovan29546

Proposal

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

App closes when switching workspaces and pressing the back button, instead of showing Inbox.

What is the root cause of that problem?

Navigation stack isn't updated correctly, causing the app to exit on back press.

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

The first block updates the stack to push SCREENS.HOME or SCREENS.INBOX after workspace switch, directing the user to the Inbox or Home screen. The second block handles back button presses, ensuring the app navigates to Inbox instead of exiting. The third block updates the central pane and handles the navigation stack based on the layout.

  1. Handle Back Button Navigation Correctly
if (checkIfActionPayloadNameIsEqual(actionForBottomTabNavigator, SCREENS.HOME)) {
    delete params.reportID;

    // Ensure the navigation stack is updated to include SCREENS.HOME or SCREENS.INBOX
    root.dispatch({
        type: CONST.NAVIGATION.ACTION_TYPE.PUSH,
        payload: {
            name: SCREENS.HOME,  // Or SCREENS.INBOX if needed
            params,
        },
    });
}
  1. Prevent App Exit on Back Press
// Handle back press fallback to prevent app exit
root.dispatch({
    type: CONST.NAVIGATION.ACTION_TYPE.PUSH,
    payload: {
        name: SCREENS.INBOX,  // Redirect to Inbox instead of closing the app
        params: {},
    },
});
  1. Central Pane Updates Correctly
if (shouldAddToCentralPane) {
    root.dispatch({
        type: CONST.NAVIGATION.ACTION_TYPE.PUSH,
        payload: {
            name: topmostCentralPaneRoute?.name || SCREENS.HOME,  // Default to HOME if undefined
            params,
        },
    });
} else {
    // If the layout is narrow, pop the stack to show the bottom tab navigator
    root.dispatch({
        type: 'POP_TO_TOP',
        target: rootState.key,
    });
}

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

  • Test workspace switching and back button behavior.
  • Verify stack integrity after multiple switches.
  • Test error scenarios (e.g., offline, app backgrounding).

What alternative solutions did you explore? (Optional)

  • Frontend fallback to redirect to Inbox.
  • Backend consistency improvements.
  • Store previous navigation stack.

saifelance avatar Dec 09 '24 02:12 saifelance

@RachCHopkins, @eh2077 Whoops! This issue is 2 days overdue. Let's get this updated quick!

melvin-bot[bot] avatar Dec 09 '24 09:12 melvin-bot[bot]

Reviewing proposals

eh2077 avatar Dec 09 '24 11:12 eh2077

I am unsure why we decided to use setParams for switching the policyID. It replaces an entry in history instead of pushing a new screen with a new policy which is an additive action. This change seems to be the root cause of this problem.

But if we assume that switching policy with setParams is the way, then your @ikevin127 proposal makes sense to me.

One thing worth mentioning is that if we go global > policyX > policyY and then back, the policy I expect would be "policyX" and the outcome is the policy will be set to global.

However, I'm not sure this is that important, considering that with the changes to setParams, history no longer works for policies anyway.

Alternatively, the second proposal by @linhvovan29546 suggests going back to the old method with PUSH instead of REPLACE. That makes even more sense to me as it should fix the history but we would need to understand which problem was supposed to be solved by using setParams

cc: @mountiny you seem to have some context about native stacks @WojtekBoman as policy police

adamgrzybowski avatar Dec 09 '24 14:12 adamgrzybowski

// If there already is a 'Home' route, we want to change the params rather than pushing a new 'Home' route, // so that the screen does not get re-mounted. This would cause an empty screen/white flash when navigating back from the workspace switcher.

I think that @chrispader @kirillzyusko added this to fix a bug they encountered, but I agree that if this ruins the history its not the best solution and we might want to rethink it.

But we need to make sure there is no regression after switching back to that.

mountiny avatar Dec 09 '24 15:12 mountiny

But if we assume that switching policy with setParams is the way

I think that's the way as it was done on purpose in this PR:

  • https://github.com/Expensify/App/pull/53102

This is the reason why I mentioned using replace in my proposal because based on that recent PR I assumed we don't want to keep history of switching in-between workspaces.

But if we do, my proposal can be flexible and details like replacing the navigation method with push instead of my proposal's suggested replace can surely be handled during PR.

I don't consider other proposals mentioning push instead of replace a significant enough difference from my proposal to constitute a new proposal given our contributor guidelines mentioning:

Note: Before submitting a proposal on an issue, be sure to read any other existing proposals. ALL NEW PROPOSALS MUST BE DIFFERENT FROM EXISTING PROPOSALS. The difference should be important, meaningful or considerable.

Another reason would be that the Expected result clearly states:

After switching workspaces and using device back button, all the chats should be displayed again on inbox.

and there were no details mentioning the expected behaviour when switching between multiple workspaces then attempting the hardware go back button.

ikevin127 avatar Dec 09 '24 19:12 ikevin127

I can throw my 2 cents here, I was responsible for making sure workspace switcher behaves correctly in search pages. I think at this point in the app (imo) we are sitting on the fence. Some places we treat WS switcher like a global switch/filter (so replace) but in others we treat it much more like a part of search history and (important) we keep it in URL.

When I was working on search I made the policyID work correctly with the data from URL, so that these steps work:

  1. go to Inbox
  2. choose a policy (id: 1) via workspace switcher
  3. go to Search (via bottom bar)
  4. in search policy is the same
  5. choose a different policy (id: 2) (it changes in the URL)
  6. press back button
  7. you land on Inbox with policy 1 because that was in the history

So as you can see at least in ☝️ flow, policyID behaves more like stacked history.

I don't have strong opinions which is better, but if we move into the direction of replace then we should check other places in the app and make them behave like that as well.

Kicu avatar Dec 10 '24 08:12 Kicu

I agree with @Kicu I think that the policyId should be kept in history so you can go back to the previously chosen policy

This was just an oversight on our part as its quite niche flow @chrispader @kirillzyusko Could you please weigh in when you get a moment?

mountiny avatar Dec 10 '24 11:12 mountiny

I think that @chrispader @kirillzyusko added this to fix a bug they encountered, but I agree that if this ruins the history its not the best solution and we might want to rethink it.

But we need to make sure there is no regression after switching back to that.

@mountiny I think the code changes to fix this issue. For the blank screen issue, it seems to be related to native-stack. However I can fix it by running Navigation.navigateWithSwitchPolicyID inside requestAnimationFrame in the JS.

linhvovan29546 avatar Dec 10 '24 12:12 linhvovan29546

@mountiny I think the problem comes from the changes made by @chrispader? If so I would wait when @chrispader returns from its vacation and can shed more light on the issue (because I'm not aware what @chrispader was attempting to solve)

For me it looks like there was a Home screen that was re-mounting somehow and @chrispader fixed it via replace calls, but it produced other issues.

After reading this conversation I think that the fix that @chrispader made could produce https://github.com/Expensify/App/issues/53359 as well (or at the problem is very highly related to each other).

Also what @Kicu explains makes sense to me. So maybe we just need to go back with push approach (assuming it will fix this problem) and check that a reported issue not reproducible anymore? In this case this:

I think the code changes to fix https://github.com/Expensify/App/pull/49937#issuecomment-2470562319. For the blank screen issue, it seems to be related to native-stack. However I can fix it by running Navigation.navigateWithSwitchPolicyID inside requestAnimationFrame in the JS.

Makes more sense for me/sounds like a better approach πŸ‘

kirillzyusko avatar Dec 11 '24 12:12 kirillzyusko

Yes, so the problem i was trying to solve was that we would see a blank HomeScreen for a brief moment the when we would change the workspace, because the screen is re-mounted and therefore re-renders.

Under the previous stack navigation, this was not causing issues, but with @react-navigation/native-stack we can see that the mounting and rendering of the screen content is still ongoing while the screen is already displayed.

I was not aware, that we want to be able to keep a "workspace history" that we can go back to. So the fix with setParams is not going to work in this case.

This is the original i and @kirillzyusko are referring to, reported by @ishpaul777: https://github.com/Expensify/App/pull/49937#issuecomment-2466304096

https://github.com/user-attachments/assets/1a2dac4c-627a-44c1-b836-b5427058ffbf

chrispader avatar Dec 11 '24 13:12 chrispader

@mountiny I think the code changes to fix this issue. For the blank screen issue, it seems to be related to native-stack. However I can fix it by running Navigation.navigateWithSwitchPolicyID inside requestAnimationFrame in the JS.

Putting Navigation.navigateWithSwitchPolicyID in requestAnimationFrame() doesn't fix the problem for me. This is what i get:

https://github.com/user-attachments/assets/1a257df9-03b3-4047-afc5-bb45f1488460

chrispader avatar Dec 11 '24 14:12 chrispader

A fix that removes the blank screen but basically just delays the route push and the content re-rendering is putting the Navigation.navigateWithSwitchPolicyID in an InteractionManager.runAfterInteractions(...). This is not as beautiful/sexy UI-wise as it was before NativeStack, but it doesn't look as weird as a blank screen:

https://github.com/user-attachments/assets/d975f37b-1e72-482b-a4cb-1afef9b7dbf8

wdyt? @mountiny @kirillzyusko

chrispader avatar Dec 11 '24 14:12 chrispader

@kirillzyusko i can confirm that the changes discussed are also causing https://github.com/Expensify/App/issues/53359. This change also fixes the issue on the search page:

https://github.com/user-attachments/assets/27f59b33-c014-4e87-af27-e64029039363

chrispader avatar Dec 11 '24 14:12 chrispader

Not overdue, valuable discussions continue!

eh2077 avatar Dec 12 '24 00:12 eh2077

@mountiny @eh2077 is somebody already assigned/working on this? Lmk if i should prepare a PR, otherwise here are the changes needed to revert my previous changes and apply the InteractionManager.runAfterInteractions() fix:

https://github.com/Expensify/App/pull/54030

chrispader avatar Dec 12 '24 12:12 chrispader

@chrispader Cool! The InteractionManager.runAfterInteractions() solution also makes sense to me. I think we can move this forward with @chrispader 's fix here https://github.com/Expensify/App/pull/54030

Edited: We can revisit this issue after reworking the original PR.

@mountiny All yours!

πŸŽ€πŸ‘€πŸŽ€ C+ reviewed

eh2077 avatar Dec 12 '24 13:12 eh2077

Triggered auto assignment to @Beamanator, see https://stackoverflow.com/c/expensify/questions/7972 for more details.

melvin-bot[bot] avatar Dec 12 '24 13:12 melvin-bot[bot]

Sounds good, lets make sure this is working fine on web too

mountiny avatar Dec 12 '24 15:12 mountiny

https://github.com/Expensify/App/pull/54030 is ready for review!

chrispader avatar Dec 15 '24 20:12 chrispader

⚠️ 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.

melvin-bot[bot] avatar Dec 24 '24 17:12 melvin-bot[bot]

Reviewing label has been removed, please complete the "BugZero Checklist".

melvin-bot[bot] avatar Dec 26 '24 20:12 melvin-bot[bot]

The solution for this issue has been :rocket: deployed to production :rocket: in version 9.0.78-6 and is now subject to a 7-day regression period :calendar:. Here is the list of pull requests that resolve this issue:

  • https://github.com/Expensify/App/pull/54030

If no regressions arise, payment will be issued on 2025-01-02. :confetti_ball:

For reference, here are some details about the assignees on this issue:

  • @chrispader does not require payment (Contractor)
  • @eh2077 requires payment through NewDot Manual Requests

melvin-bot[bot] avatar Dec 26 '24 20:12 melvin-bot[bot]

@eh2077 @RachCHopkins @eh2077 The PR fixing this issue has been merged! The following checklist (instructions) will need to be completed before the issue can be closed. Please copy/paste the BugZero Checklist from here into a new comment on this GH and complete it. If you have the K2 extension, you can simply click: [this button]

melvin-bot[bot] avatar Dec 26 '24 20:12 melvin-bot[bot]