[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
Conversation
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>
Contributor
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 |
Contributor
Author
@myabc Do you think it’s safe to use this version in the meantime? |
myabc
approved these changes
Jul 6, 2026
myabc
left a comment
Contributor
There was a problem hiding this comment.
I haven't had a chance to test the actual fix, but looks fine code-wise!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 fromturbo_power, which calls rawwindow.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'spopstatehandler 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_statestream 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 emittingturbo_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.pushis the public, typed API (Turbo.navigator.historyis 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/titleattributes).No infinite-loop risk: a Back-triggered restoration visit is a plain HTML GET, so the controller's
format.htmlbranch runs and never emits apush_statestream.Merge checklist