Files
1b04e697f6 All Mail: every conversation outside Spam and the bin, as an opt-in system label (#57)
Archived mail had no list to be browsed in, only search. All Mail lists every conversation outside Spam and the bin (what arrived, what was sent, drafts and archived mail), as Gmail's All Mail does, across the reader's active accounts or within one.

It is LabelRole::AllMail, a view and not a place, like Unread: nothing attaches it to mail, it is created hidden when label settings is opened, it is bound to no account and it refuses moves. Its eye in Settings → Mail → Labels shows or hides both the row beside Drafts and the row at the top of each account's folders. Archiving or snoozing from it leaves the row in place. One list scope, `all_mail`, with the account id as its value or empty for every account; AllMailView holds the rules the row buttons, the bulk toolbar, Move to and select-all ask.

No migration.

Co-authored-by: shady2k <3366415+shady2k@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 09:36:25 +02:00

68 lines
3.2 KiB
Twig
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{#
The answer to a bulk action: redraw the list, once.
Not a stream per row, which is what the toolbar's per-conversation posts
produced. Each of those knew what had happened to its own row and nothing else,
so a list that lost four of five rows still said "1–5 of 5", and one that lost
every row showed no empty state at all — the pager and the placeholder are
properties of the LIST, and no row-shaped response can carry them.
So this body carries no streams at all: the CLIENT reloads the list frame when
the request comes back, and the controller answers that reload exactly as it
answers any other visit to the list. One definition of what the list looks like,
and no second implementation here of the paging, filtering and sorting this
response has no parameters for.
A `<turbo-stream action="refresh">` was tried first and is not reliable enough
for this: it asks Turbo to refresh the PAGE, which is debounced and skipped
under conditions this code cannot see. Rows that leave the list happened to look
right — they were gone either way — while rows that STAY, like a conversation
being marked read, kept their old state on screen until something else
re-rendered them.
The count is a comment rather than anything visible. Nothing on screen needs it,
and a bulk action that silently does nothing is otherwise indistinguishable from
one that worked — which is exactly how a whole-view archive that resynced only
the first thread's labels answered 200, reported 71, and changed nothing.
## Why the rows are here as well as the frame reload
The rows are redrawn from here AND the client re-reads the list frame, which
looks like doing the same work twice and is not. The rows have to change the
moment the response lands, because that is the feedback for the click; the frame
has to be re-read because the pager and the empty state are properties of the
list that no row-shaped stream can carry. Leaving out the first made a bulk
mark-read take a visible beat and left the rows stale in between; leaving out
the second is the original finding.
Variables:
count how many conversations were acted on; 0 is a valid answer
threads the conversations, for redrawing their rows
leaves true when the action takes them out of THIS list (archive, trash,
restore) rather than changing them in place (read)
undoToken optional; when set the action can be undone, and says so in a
toast — see _undo_toast.stream.html.twig
undoToast the translation key for that toast's sentence
#}
<!-- plmail-bulk: {{ count }} -->
{% for thread in threads %}
{% if leaves %}
<turbo-stream action="remove" target="thread_{{ thread.id }}">
</turbo-stream>
{% else %}
<turbo-stream action="replace" target="thread_{{ thread.id }}">
<template>
{% include '_partials/_thread_row.html.twig' with { thread: thread, unified: unified ?? true } only %}
</template>
</turbo-stream>
{% endif %}
{% endfor %}
{% if undoToken|default(null) is not null and count > 0 %}
{% include 'thread/status/_undo_toast.stream.html.twig' with {
message: undoToast|trans,
undoToken: undoToken,
} only %}
{% endif %}