mirror of
https://github.com/karatektus/pl_mail.git
synced 2026-10-25 10:04:18 +00:00
Reported as the counter updating "nicht zuverlässig" when mail is read. Unreliably rather than never, because navigating between folders re-renders the badges server-side — so whether it looked broken depended on whether you happened to click away afterwards. It was never unreliable. The badges had exactly one immediate trigger, `mail--list-toolbar:written`, and only the BULK toolbar ever dispatched it. So they kept up with the one path almost nobody uses and with none of the paths people actually read mail through: opening a conversation (mail--thread-read), the envelope button on a row (mail--message-row), and the reading pane's overflow menu (mail--message-actions). All three POST to the same status endpoints, redraw their row from the returned Turbo Stream, and told nobody else. Measured on the inbox after reading one mail: a full minute, no sync event, no counts request, the badge still one too high — while /mail/sidebar/ counts had the right number the whole time. Only a reload put it right. The announcement now belongs to everybody, in assets/mail_writes.js. A write path that forgets to call it has the same bug, but it has it in a form somebody can grep for. Deliberately NOT folded into the toolbar's existing `:writing`/`:written` pair, which is untouched. That pair is a different statement — "hold the list frame refresh, I am part-way through a run of posts" — answered by mail--mail-pane with a fetch of the whole frame. Firing it from mail--thread-read would re-read the entire list on every mail opened, which is the commonest action in the app. This event asks only for the counts, which is the thing that was stale. thread-read announces only when the read moved a number. It marks read on every open, including mail already read, and that is by far the commoner case once an inbox is under control — those posts change nothing, so announcing them would put a counts request behind every click on a mail. The sidebar's rate limiting exists because this endpoint was once asked thirty-two times in ten seconds; this keeps that honest, and live-counts.spec.ts pins it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
58 lines
2.0 KiB
JavaScript
58 lines
2.0 KiB
JavaScript
import { Controller } from "@hotwired/stimulus";
|
|
import { jsonCsrfHeaders } from "../../csrf.js";
|
|
import { announceWrite } from "../../mail_writes.js";
|
|
|
|
export default class extends Controller {
|
|
static values = {
|
|
id: Number,
|
|
delay: { type: Number, default: 2000 },
|
|
markUrl: String,
|
|
/**
|
|
* Whether this conversation was still unread when the page rendered.
|
|
*
|
|
* Defaults TRUE, so a caller that does not say assumes the read moved
|
|
* a number and announces it. A missing announcement is the bug this
|
|
* whole change is about; a spare one costs a small JSON fetch.
|
|
*/
|
|
wasUnread: { type: Boolean, default: true },
|
|
};
|
|
|
|
#timer = null;
|
|
|
|
connect() {
|
|
this.#timer = setTimeout(() => this.#markRead(), this.delayValue);
|
|
}
|
|
|
|
disconnect() {
|
|
// User navigated away before the delay elapsed — cancel.
|
|
clearTimeout(this.#timer);
|
|
}
|
|
|
|
async #markRead() {
|
|
const response = await fetch(this.markUrlValue, {
|
|
method: "POST",
|
|
headers: jsonCsrfHeaders(),
|
|
body: JSON.stringify({ read: true }),
|
|
});
|
|
|
|
if (!response.ok) return;
|
|
|
|
const html = await response.text();
|
|
Turbo.renderStreamMessage(html);
|
|
|
|
// The stream above re-renders the list ROW. The sidebar badge this
|
|
// mail was just counted in is somewhere else entirely and hears
|
|
// nothing from a stream, so it is told. This is the single biggest
|
|
// source of the stale counter: opening a mail is how mail gets read.
|
|
//
|
|
// Only when the read actually moved a number. This controller marks
|
|
// read on every open, including mail that was already read, and that
|
|
// is by far the commoner case once an inbox is under control — those
|
|
// posts change nothing, so announcing them would put a counts request
|
|
// behind every click on a mail for no number that could differ.
|
|
if (true === this.wasUnreadValue) {
|
|
announceWrite();
|
|
}
|
|
}
|
|
}
|