Skip to content

[OP-19345] Impossible to go back with single click from the user profile to the users list when a filter is added#24076

Open
dfriquet wants to merge 1 commit into
devfrom
bug/op-19345-impossible-to-go-back-with-single-click-from-the-user-profile-to-the-users-list-when-a-filter-is-added
Open

[OP-19345] Impossible to go back with single click from the user profile to the users list when a filter is added#24076
dfriquet wants to merge 1 commit into
devfrom
bug/op-19345-impossible-to-go-back-with-single-click-from-the-user-profile-to-the-users-list-when-a-filter-is-added

Conversation

@dfriquet

@dfriquet dfriquet commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Ticket

https://community.openproject.org/wp/OP-19345

What are you trying to accomplish?

On the users list (and the other filtered list pages), it took several Back-button clicks to return from a user's page to the list: the address bar updated to the expected URL, but the page content didn't change until an extra click or two.

The live filter/sort/page mechanism updates the list in place via a turbo-stream response that ends with turbo_stream.push_state(url) to sync the address bar. That stream action comes from turbo_power, which calls raw window.history.pushState(state, ...) with an empty state — so the entry lacks Turbo's { turbo: { restorationIdentifier } } key. When you press Back onto such an entry, Turbo Drive's popstate handler takes the empty-state branch (historyPoppedWithEmptyState), which only rewrites the URL and never renders. Each filter change adds one of these "dead" entries, so Back has to step through them one URL-change at a time before hitting a real Turbo entry that actually renders.

This overrides the push_state stream action so it routes through Turbo's own history (Turbo.session.history.push). The entry then carries restoration data, and Back triggers a proper restoration visit that renders the page. Because the action is shared, this fixes all six filtered list pages at once (users, projects, portfolios, meetings, documents, project-reserved-identifiers) with no controller changes — controllers keep emitting turbo_stream.push_state(url).

What approach did you choose and why?

An earlier iteration fixed only the users page by switching the controller to turbo_stream.replace_state (collapsing the list to a single history entry). That was discarded: we want a distinct pushed state per filter change and a working Back button — the state-per-filter behaviour is intentional, only its restoration was broken.

Fixing the shared stream action rather than each controller keeps the change in one place and covers all six pages identically. Turbo.session.history.push is the public, typed API (Turbo.navigator.history is internal and untyped).

This is the same defect and remedy as upstream turbo_power #11 / #339. We override locally instead of waiting on that change, which is unmerged and carries a breaking change (dropping the state/title attributes).

No infinite-loop risk: a Back-triggered restoration visit is a plain HTML GET, so the controller's format.html branch runs and never emits a push_state stream.

Merge checklist

  • Added/updated tests
  • Added/updated documentation in Lookbook (patterns, previews, etc)
  • Tested major browsers (Chrome, Firefox, Edge, ...)

turbo_power's push_state wrote a state-less history entry that Turbo's
popstate handler ignores, so Back only rewrote the URL without rendering.
Route it through Turbo's own history instead.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@dfriquet dfriquet changed the title [OP-19345] Make push_state Turbo-aware so Back restores filtered list pages [OP-19345] Impossible to go back with single click from the user profile to the users list when a filter is added Jul 2, 2026
@dfriquet dfriquet marked this pull request as draft July 2, 2026 15:40
@myabc

myabc commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

@dfriquet

This is the same defect and remedy as upstream turbo_power marcoroth/turbo_power#11 / marcoroth/turbo_power#339; we override locally rather than wait on the (unmerged, breaking) upstream change.

I left the upstream change draft as I'd prefer to get some feedback from the maintainer. It may be that he requests a separate turbo_push_state action to avoid the breaking change - although my preference would be for Turbo Power actions to work "nicely" with Turbo Drive out-of-the-box.

@dfriquet

dfriquet commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

I left the upstream change draft as I'd prefer to get some feedback from the maintainer.

@myabc Do you think it’s safe to use this version in the meantime?

@dfriquet dfriquet changed the base branch from dev to release/17.6 July 3, 2026 07:48
@dfriquet dfriquet changed the base branch from release/17.6 to dev July 3, 2026 07:59
@dfriquet dfriquet marked this pull request as ready for review July 3, 2026 08:25

@myabc myabc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't had a chance to test the actual fix, but looks fine code-wise!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants