aboutsummaryrefslogtreecommitdiffstats
path: root/tests/fixtures/inline_image.eml
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-08-22 10:32:44 +0200
committerDanilo M. <danix@danix.xyz>2026-08-22 10:49:07 +0200
commit141bc9b98213b9c4ba9f3bcba041c3e3108c12a3 (patch)
tree2ed6f867723ed201631696d089df206868852cba /tests/fixtures/inline_image.eml
parentd5ee2ab07d5313d153f62ce2ed8dcd027c677a09 (diff)
downloadqtmaildir-141bc9b98213b9c4ba9f3bcba041c3e3108c12a3.tar.gz
qtmaildir-141bc9b98213b9c4ba9f3bcba041c3e3108c12a3.zip
feat(compose): the composer window, item 123
A separate top-level QMainWindow, one per draft, several open at once. A modal dialog cannot consult another message while writing, which is most of what replying is, and taking over the message pane fights the pane that exists to show what is being replied to. No geometry save and no restore, deliberately. Under a tiling compositor saveGeometry stores normalGeometry while the compositor owns the tile, so the restore is correct and looks broken; a whole session went into that once. Autosave is a debounce AND a dirty check: an unchanged message writes no file and provokes no sync. The check is on a fingerprint of the OutgoingMessage, NOT on the built bytes as the plan drafted. GMime is given a fresh Date and Message-ID on every build, so two builds of an unchanged message never compare equal; a check on the bytes would have read as working while writing a file, and an mbsync upload, on every debounce. Checking before the build also skips the blocking build for the no-change case, which is the common one. closeEvent writes the draft when the buffer is dirty. Without it the debounce is a hole rather than a delay: typing a paragraph and pressing the window manager's X inside the interval loses it silently, since WA_DeleteOnClose destroys the window immediately afterwards. A failed save there does NOT refuse the close, because a window that will not close because it cannot save is worse than one that closes having raised the banner, which is what the quit path reads. One flag covers a send, countdown included. An earlier revision had two, and the narrower "committed and running" one reads as the honest thing to guard a live SMTP conversation with. It is not: a close during the countdown destroys the parented SendDialog, committed() never fires, and the user pressed Send, watched a countdown, and believes the mail went. The narrow flag was also written in three places and read in none. A failed draft write raises a persistent banner rather than a modal or a fading status line. A modal mid-sentence is hostile while the user is typing, but the warning must survive until it is dealt with, because the quit path escalates exactly this state to a dialog on the way out. An account with no drafts folder reports success rather than failure: nothing was written and nothing failed, and a false there would make the quit path offer a retry no retry can change. A failed send saves the draft before reporting. send() builds from the widgets without saving, so the revision on disk is whatever the last debounce wrote: edit, send, fail, close, and the user gets the older text back, having watched their correction be sent. A failed sent copy after a successful send is a modal, and never a send failure: the message went, and reporting otherwise makes someone send it twice. It is the one failure here that silently diverges what the recipient received from what the local archive shows, and nobody discovers a missing sent copy by noticing a line that appeared for a few seconds. The formatting toolbar applies its edits through a QTextCursor document replacement inside one edit block, NOT setPlainText as the plan drafted. Measured against a real widget: setPlainText destroys the document's undo stack and resets the cursor to 0, so every toolbar press would throw away everything the user could undo. The cursor route leaves undo available, collapses to a single undo step, and emits textChanged once. The seeded quote is cleared off the undo stack afterwards, since it is not an edit the user made and one Ctrl+Z on a fresh composer must not wipe it. The per-send connect carries Qt::SingleShotConnection. MessageSender is a long-lived member, so a bare connect accumulates a permanent receiver per send and the second result runs both lambdas, the first still holding the first message's bytes: it files a sent copy of the wrong message and acts on a dialog it already destroyed. Covered by a test that sends, fails, corrects and sends again; without the flag it segfaults in QLabel::setText on the destroyed dialog. Its companion disconnect takes the specific connection handle rather than every finished receiver on this object, so a later observer cannot be killed silently. The attachment warning states sizes with a decimal and a stepped unit. Integer MB division read as "'x' is 0 MB. Many mail servers refuse messages above about 0 MB." for any attachment_warn_bytes below a megabyte, in both halves of one sentence. The autosave timer is created before buildUi(), which is load-bearing: buildUi connects every field to markDirty and seeding then fills those fields, so markDirty runs during construction. Created afterwards it is a null dereference on the first seeded field, which is every composer. Twenty-six cases in test_mainwindow, each mutation-checked. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'tests/fixtures/inline_image.eml')
0 files changed, 0 insertions, 0 deletions