Skip to content

[$250] Native share - Android - No sharing flow/app freezes when share image signed out #101425

Open

Description

@applause-bot

If you haven’t already, check out our contributing guidelines for onboarding. To join our Slack channel, fill out this form.


Version Number: 9.4.81-0
Reproducible in staging?: Yes
Reproducible in production?: Yes
If this was caught during regression testing, add the test name, ID and link from BrowserStack: https://test-management.browserstack.com/projects/2219752/test-runs/74212640/folder/13176721/41236968/1766200870?sort=%2Bstatus_id
Email or phone of affected tester (no customers): gocemate+buzz19953@gmail.com
Issue reported by: Applause Internal Team
Bug source: Regression TC Execution
Device used: samsung galaxy a34 5g/16
App Component: Other

Action Performed:

Preconditions:
The Expensify app is installed
User is not signed in

Scenario 1:

  1. Select a photo from device gallery
  2. Share it to Expensify app
  3. Verify the login screen for Expensify app displayed
  4. Login with new user with private domain

Scenario 2:

  1. Select a photo from device gallery
  2. Share it to Expensify app
  3. Verify the login screen for Expensify app displayed
  4. Login with new user with public domain

Expected Result:

After signing you are redirected to the sharing flow

Actual Result:

Scenario 1: App freezes on sharing page

Scenario 2: After signing in share modal appears and disappears when onboarding modal show up. Also, there is a error messages on onboarding modal

Workaround:

Unknown

Platforms:

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

Screenshots/Videos

Bug7261874_1789592511813.Recording__1641.mp4
Bug7261874_1789634712783.ScreenRecording_09-16-2026_23-52-51_1.mp4

View all open jobs on GitHub

Applause Internal Information
Exported by: Svetlana Lazutkina
Bug ID: 7261874

Issue OwnerCurrent Issue Owner: @abbasifaizan70
Upwork Automation - Do Not Edit

Activity

  1. added
    BugSomething is broken. Auto assigns a BugZero manager.
    on Sep 17, 2026
  2. MelvinBot commented on Sep 17, 2026

    @MelvinBot
    Contributor

    Proposal

    What is the root cause of that problem?

    The Android share intent is delivered as a plain deep link (new-expensify://share/root) with no session or onboarding awareness (FileIntentHandler.kt:36-41). When the user is signed out, that route does not exist yet (SHARE_MODAL_NAVIGATOR is only registered in AuthScreens), so the deep link is parked and replayed after sign-in by openReportFromDeepLink. For a brand new user, three separate things then break it:

    1. The share deep link is deliberately dropped for new users. Link.ts:613-617 returns early whenever hasCompletedGuidedSetupFlow was false at sign-in, on the assumption that navigateAfterOnboarding owns the destination. But navigateAfterOnboarding only knows about reports and Home — it has no notion of a pending share, so the shared file is abandoned. That is the "no sharing flow" half.
    2. If the share modal does get mounted, onboarding amputates it. NAVIGATORS.SHARE_MODAL_NAVIGATOR is listed in MODAL_ROUTES_TO_DISMISS, and RootStackRouter.ts:136-160 slices the stack at the first such route whenever OnboardingGuard issues a redirect. The share flow is deleted, not stacked — "share modal appears and disappears".
    3. The error text on the onboarding modal is set by the deep link handler itself. Link.ts:599-603 sets onboarding.purpose.errorBackButton ("Please finish the setup questions to start using the app") when the replayed link resolves while an onboarding screen is focused. A public-domain (gmail) user lands on OnboardingWorkEmail, whose Android back handler sets the same error — private-domain users land on a screen that swallows back silently, which is why only Scenario 2 shows errors.

    Why Scenario 1 (private domain) freezes instead: the onboarding navigator is registered as a transparent, gestureEnabled: false modal (AuthScreens.tsx:352-363) and renders null while account metadata loads (OnboardingModalNavigator.tsx:109-112). The share page stays visible underneath but every touch goes to the empty focused screen on top — an apparent freeze, not a crash.

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

    1. Don't drop a pending share intent for new users. Exempt ROUTES.SHARE_ROOT from the early return at Link.ts:613-617, and instead persist it as a pending destination that navigateAfterOnboarding consumes once guided setup finishes. This keeps the anti-flash behaviour that early return was added for while making the share flow survive onboarding.
    2. Suppress the misleading onboarding error for this path. At Link.ts:599-603, don't call setOnboardingErrorMessage when the deep link is a share intent — the user never pressed back, so "Please finish the setup questions" is wrong.
    3. Make ShareRootPage fail loudly instead of hanging. Wrap the JSON.parse(processedFiles) at ShareRootPage.tsx:103 in a guard and set an error state. The native module invokes the callback with the bare string "No data found" when the prefs are empty (ShareActionHandlerModule.kt:32), which throws inside the callback, so isFileReady never flips and the page renders TabNavigatorSkeleton forever with no retry.
    4. Add Share_Root to EXCLUDE_FROM_LAST_VISITED_PATH (runtimeDefaults.ts:92-109). /share/root is currently persisted and restored on a later cold start, where there is no share payload at all — restoration is skipped for public-domain accounts (NavigationRoot.tsx:163-165), which is a second way private-domain users specifically reach a permanently skeletal share page.

    What alternative solutions did you explore? (Optional)

    Alternatives considered and evidence

    Alternative: defer onboarding while a share intent is pending. There is no such guard today — startOnboardingFlow deliberately bypasses the router with a raw navigationRef.resetRoot (OnboardingFlow.ts:91-108, called from useOnboardingFlow.ts:135-139) precisely because OnboardingGuard would block a normal navigate. Letting share pre-empt onboarding would mean a new user could post an attachment before having an account set up, so resuming after onboarding (fix 1) is safer.

    Alternative: gate the native intent on session state. The native handler could check for an auth token before firing the deep link and show a "sign in first" screen. This duplicates session logic in Kotlin and still needs the JS-side resume, so it doesn't remove any of the work above.

    Ruled out: a native ANR / main-thread stall. The reported symptom is a freeze on the Expensify sharing page, i.e. JS already rendered the share screen, so the intent handling and file copy completed.

    Timing note. The share modal is visible at all in Scenario 2 because OnboardingGuard allows navigation while ONYXKEYS.IS_LOADING_APP is true; once OpenApp returns, the reset/redirect above tears it down. Exactly which of the two teardown paths wins is a race, so the ordering (but not the mechanism) is unconfirmed.

    Not verified: the report is build 9.4.81-0 on a Samsung device, which is likely HybridApp. All the JS fixes above are in this repo, but if HybridApp ships its own MainActivity/manifest from Mobile-Expensify, the native half of the intent path should be re-checked there. I could not inspect that repo.

    Possibly related issues


    Next Steps for Contributor+ team:
    To accept: @MelvinBot implement [this](https://github.com/Expensify/App/issues/101425) to create a draft PR.
    To refine: @MelvinBot <your feedback>
    To reject: Explain why you are rejecting Melvin's proposal.


    view run

  3. added
    ExternalAdded to denote the issue can be worked on by a contributor
    on Sep 17, 2026
  4. melvin-bot commented on Sep 17, 2026

    @melvin-bot

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

  5. melvin-bot commented on Sep 17, 2026

    @melvin-bot

    Unable to auto-create job on Upwork. The BZ team member should create it manually for this issue.

  6. melvin-bot commented on Sep 22, 2026

    @melvin-bot

    @jayeshmangwani Huh... This is 4 days overdue. Who can take care of this?

  7. melvin-bot commented on Sep 24, 2026

    @melvin-bot

    @jayeshmangwani Still overdue 6 days?! Let's take care of this!

  8. jayeshmangwani commented on Sep 25, 2026

    @jayeshmangwani
    Contributor

    Melvin, let’s raise a draft PR so I can test the changes on my side and see how they work.

  9. jayeshmangwani commented on Sep 25, 2026

    @jayeshmangwani
    Contributor

    @MelvinBot implement this

  10. 69 remaining items

  11. removed
    Help WantedApply this label when an issue is open to proposals by contributors
    on Oct 28, 2026
  12. melvin-bot commented on Oct 28, 2026

    @melvin-bot

    📣 @abbasifaizan70 You have been assigned to this job!
    Please apply to the Upwork job and leave a comment on the Github issue letting us know when we can expect a PR to be ready for review 🧑‍💻
    Once you apply to this job, your Upwork ID will be stored and you will be automatically hired for future jobs!
    Keep in mind: Code of Conduct | Contributing 📖

  13. FitseTLT commented on Oct 28, 2026

    @FitseTLT
    Contributor

    @jayeshmangwani @Gonals Thanks for the review. I'd like you to reconsider, because my proposal isn't a workaround. It extends a mechanism we already have for this exact situation.

    This isn't a new behaviour. Opening a destination the user chose before signing up, once onboarding finishes, is something the app already does for public rooms. That was fixed in #92834 (issue #85242). We keep the deep-linked page in the stack underneath the onboarding redirect, so onboarding shows first and the user lands back on it afterwards:

    Root cause. The share fails because this same setup doesn't account for a share:

    1. The modal-redirect branch cleans the share away along with the other modals.
    2. navigateAfterOnboarding then pushes Home over whatever the user had open.

    My fix teaches those two spots about the share, and nothing else:

    • the router keeps ShareModalNavigator under the onboarding modal;
    • dismissOnboardingModalBeforeExit doesn't dismiss a modal that isn't onboarding;
    • navigateAfterOnboarding skips the Home push while a share is open.

    As the video shows, onboarding comes first and the user lands back on the share afterwards. That's the same experience as the public-room flow.
    Test Branch
    WDYT @MelvinBot

    2026-10-28.19-23-02.mp4
  14. mukhrr commented on Oct 28, 2026

    @mukhrr
    Contributor

    As the video shows, onboarding comes first and the user lands back on the share afterwards. That's the same experience as the public-room flow.

    I see Share coming first (1:13) in your video before onboarding and I think this should be fixed on public-room flow as well

  15. MelvinBot commented on Oct 28, 2026

    @MelvinBot
    Contributor

    I don't think this changes the decision. The public-room mechanism keeps a fullscreen route underneath onboarding, but it deliberately drops modals, and ShareModalNavigator is a modal. Keeping it there is a new exception to that rule. It also leaves three gaps that the selected proposal covers:

    1. The share still shows before onboarding on cold start. Your fix only works if the share is already on the stack when onboarding is added. That flash is the reason the C+ reviewer gave for choosing the other proposal.
    2. Several onboarding exits still cover the share. Only the two Home pushes check for an open share (L154, L182). These exits still push a screen on top of it:
      • Track → Concierge (L160-L163). The reviewer already flagged this case on another proposal.
      • Last accessed report (L181).
      • Submit workspace (L233).
    3. Warm start isn't handled by this change, and there are no tests. The branch doesn't touch Link.ts, so a share that arrives while the app is already running depends on main's existing deep-link replay.
    Details
    • The mechanism drops modals on purpose. RootStackRouter.ts:132-138 cuts the stack at the first route in MODAL_ROUTES_TO_DISMISS, which includes SHARE_MODAL_NAVIGATOR. The comment there explains why: it prevents two stacked modals (Web - Onboarding - Two Expensify logos appear on the modal). fix: land new account on initially opened room added this branch, and its test checks that the SignIn modal is dropped while the fullscreen report is kept. A public room survives because it is a fullscreen route, not a modal.
    • The existing guards check fullscreen routes only. isReportTopmostSplitNavigator() asks whether the top fullscreen tab is Reports, so it can't tell whether a modal is open. You had to add hasOpenShareModal() as a new check.
    • The dismiss change follows from the router change. dismissOnboardingModalBeforeExit is safe today only because the router makes onboarding the only modal. Your router change removes that guarantee, so the dismiss needs a guard too.
    • Warm start: when main's replay from Reapply opening a deep link captured before onboarding, without the Concierge regressions parks the share, consumeDeepLinkToOpenAfterOnboarding() opens it at L139 and skips the router change. I traced this in code only and didn't test it on a device.
    • Note for the assigned contributor: the selected proposal's branch predates that replay, so it needs a merge with main.
  16. FitseTLT commented on Oct 28, 2026

    @FitseTLT
    Contributor

    30. > As the video shows, onboarding comes first and the user lands back on the share afterwards. That's the same experience as the public-room flow.

     I see Share coming first (1:13) in your video before onboarding and I think this should be fixed on public-room flow as well
    

    This is not new behavior even on large screen onboarding modal opens on top of the underlying screen whethere it is Home or Inbox. When it is native on emulator it is slow and takes time before the modal opens even in normal case.

  17. jayeshmangwani commented on Oct 29, 2026

    @jayeshmangwani
    Contributor

    81. > 28. Thanks, everyone, for the proposals! I think @abbasifaizan70 's proposal #101425 (comment) looks good, as it avoids showing the Share page before onboarding is complete. The onboarding flow completes first, and then we see the Share page.
    > 🎀 👀 🎀 C+ reviewed

     [@jayeshmangwani](https://github.com/jayeshmangwani) thanks for the review. Could you pls give feedback what is wrong with [this proposal](https://github.com/Expensify/App/issues/101425#issuecomment-6068417543). I already included flash onboarding on alternative solution. thanks! just curios
     > Filtering share/root out of React Navigation's native linking while signed out. That would remove the flash,
    

    @mukhrr Nothing wrong with your proposal! I tested it, and it has the same behavior as the other proposals: the Share with expensify page appears before the onboarding flow. That’s why I decided to go with this approach #101425 (comment).

  18. melvin-bot commented on Oct 29, 2026

    @melvin-bot

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

  19. abbasifaizan70 commented on Nov 2, 2026

    @abbasifaizan70
    Contributor

    @Gonals @jayeshmangwani The PR is ready for the review. Thanks

  20. jeffrey-graham commented on Nov 7, 2026

    @jeffrey-graham
  21. melvin-bot commented on Nov 7, 2026

    @melvin-bot

    📣 @jeffrey-graham! 📣
    Hey, it seems we don’t have your contributor details yet! You'll only have to do this once, and this is how we'll hire you on Upwork.
    Please follow these steps:

    1. Make sure you've read and understood the contributing guidelines.
    2. Get the email address used to login to your Expensify account. If you don't already have an Expensify account, create one here. If you have multiple accounts (e.g. one for testing), please use your main account email.
    3. Get the link to your Upwork profile. It's necessary because we only pay via Upwork. You can access it by logging in, and then clicking on your name. It'll look like this. If you don't already have an account, sign up for one here.
    4. Copy the format below and paste it in a comment on this issue. Replace the placeholder text with your actual details.
      Screen Shot 2022-11-16 at 4 42 54 PM
      Format:
    Contributor details
    Your Expensify account email: <REPLACE EMAIL HERE>
    Upwork Profile Link: <REPLACE LINK HERE>
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

BugSomething is broken. Auto assigns a BugZero manager.DailyKSv2ExternalAdded to denote the issue can be worked on by a contributorReviewingHas a PR in review

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions