feat(mail): switching category tabs replaces the history entry, as in Gmail (#161)

* feat(mail): replace the history entry when switching category tabs on a phone

Below md, a category tab navigates the inbox list frame with Turbo's
replace action, so the browser's Back leaves the inbox instead of
stepping back through every tab. At md and up the link keeps advance,
so desktop behaviour is unchanged.

The choice is made on click by the ui--tab-history controller, not on
connect: the tab strip is morphed on every list refresh, and a morph
removes attributes the server did not render.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>

* feat(mail): category tabs replace the history entry at every width

#150 asked for this on a phone only; the maintainer wants it everywhere,
as Gmail does. With no breakpoint to ask about, the tab links render
data-turbo-action="replace" from the server, so the click-time controller
goes: a server-rendered attribute survives the strip's morph on its own.
The account page's tab strip, added on main since, gets the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(changelog): credit the tab history entry to #161, which took over #113

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Flouss <15911342+Flouss@users.noreply.github.com>
Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
authored and GitHub committed 2026-10-15 14:44:47 +02:00
1 parent 4738d514dd
commit 280f58b7b5
7 files changed
+205 -1

No files matched your search

+2
View File
@@ -53,6 +53,8 @@ it and from the account's other folders beside it.
<a
href="{{ path('app_mail_account', {account: account.id}|merge(tabQuery)|merge({tab: tabCase.value})) }}"
data-turbo-frame="inbox-list-frame"
{# `replace`, as on the Inbox's strip: see the note there. #}
data-turbo-action="replace"
{% if isActive %}aria-current="page" data-ui--scroll-active-target="current"{% endif %}
class="flex items-center gap-2.5 px-3 @xl:px-5 py-1.5 min-h-14 font-medium border-b-2 -mb-px transition-colors shrink-0 whitespace-nowrap {{ isActive ? 'text-accent border-accent' : 'text-ink-muted hover:text-gray-700 border-transparent hover:border-gray-300' }}"
>
+9
View File
@@ -170,9 +170,18 @@
accepting it would remove the row from the list and put it
back on the next refresh, which reads as a glitch rather than
as "nothing to do". #}
{# `replace`, overriding the frame's `advance`: a tab switch is not a
page of its own, so it takes the current history entry rather
than adding one, as Gmail's tabs do. Otherwise every tab merely
looked at was one more Back step before Back left the inbox.
The URL still follows the tab, so a reload or a copied link
opens it. Rendered by the server rather than set from script,
because the strip is morphed on every list refresh and a morph
drops any attribute the server did not render. #}
<a
href="{{ path('app_mail_inbox', tabQuery|merge({tab: tabCase.value})) }}"
data-turbo-frame="inbox-list-frame"
data-turbo-action="replace"
{% if false == isActive %}data-dnd-category="{{ tabCase.value }}"{% endif %}
{% if isActive %}data-ui--scroll-active-target="current"{% endif %}
class="flex items-center gap-2.5 px-3 @xl:px-5 py-1.5 min-h-14 font-medium border-b-2 -mb-px transition-colors shrink-0 {{ isActive ? 'text-accent border-accent' : 'text-ink-muted hover:text-gray-700 border-transparent hover:border-gray-300' }}"