aboutsummaryrefslogtreecommitdiffstats
AgeCommit message (Collapse)AuthorFilesLines
6 daysdocs: document the calendar, translate it, file its follow-upsDanilo M.9-23/+765
Give RepeatRule its own Q_DECLARE_TR_FUNCTIONS context so lupdate extracts describe() and the ordinal words, mirroring the array's QT_TRANSLATE_NOOP; without it lupdate warned and RepeatRule/last was never extractable.
6 daysfeat: open the calendar from the View menu and the toolbarDanilo M.4-0/+87
6 daysfix: add the toolbar New button, sync the month controls, and let a stale ↵Danilo M.3-4/+81
save be retried
6 daysfeat: the calendar window, with undo and a stale-safe saveDanilo M.5-0/+1089
6 daysfix: show the collection swatch and translate the attendee line in the event ↵Danilo M.2-3/+16
pane
6 daysfeat: show and edit a calendar event in the side paneDanilo M.3-0/+621
6 daysfix: swallow the calendar more-line double-click and clear the agenda selectionDanilo M.2-2/+14
6 daysfeat: paint the calendar's month grid and agenda listDanilo M.5-0/+387
6 daysfeat: lay out the calendar's month grid without a painterDanilo M.5-0/+221
6 daystest: a write during a run queues one more calendar syncDanilo M.2-4/+39
6 daysfeat: debounce the calendar sync command after writesDanilo M.5-0/+221
6 daystest: pin the calendar config's reported error pathsDanilo M.1-0/+50
6 daysfeat: read the calendar's config keysDanilo M.3-0/+102
6 daysfeat: write calendar files atomically, refusing stale onesDanilo M.5-0/+233
6 daystest: an occurrence delete keeps the exdates already thereDanilo M.1-0/+19
6 daysfeat: edit or delete one occurrence of a repeating eventDanilo M.2-3/+122
6 daysfix: compare sameMeaning overrides by recurrence id and check the uidDanilo M.2-2/+50
6 daysfeat: edit a calendar event in place and write new onesDanilo M.2-3/+412
6 daysfeat: an event is editable only when the user organises itDanilo M.2-1/+41
6 daysfix: expand a long dense series completely and flag overrides' own all-dayDanilo M.2-4/+39
6 daysfeat: expand calendar occurrences in each event's own zoneDanilo M.2-2/+249
6 daysfix: an empty RRULE means does-not-repeat, not a custom ruleDanilo M.2-0/+16
6 daysfeat: model the calendar's repeat control as RRULE textDanilo M.2-4/+361
6 daysfeat: read every time form and load a calendar vdirDanilo M.2-1/+195
6 daysfeat: parse one calendar event with libical (item 206)Danilo M.11-1/+630
6 daysdocs: plan the calendar window (item 206)Danilo M.1-0/+5468
Sixteen TDD tasks from the libical build wiring to the hand-test hand-off. Four rulings against the spec are recorded at the top: calendars_dir absent means off, preserved properties are compared by value rather than bytes, views read CalendarItem, and expansion runs from DTSTART without icalrecur_iterator_set_start. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 daysdocs: close backlog item 204, contact completion shippedDanilo M.2-82/+100
Its section moves to the closed-items file with the execution record, and the status row points there. The reconcile against the user's notes found every open note line already covered by an existing item. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 daysdocs: specify the calendar window (item 206)Danilo M.1-0/+372
Month and agenda views over the vdirsyncer vdir, editing in the side pane, atomic stale-checked writes with a debounced sync and a post-sync comparison, and undo for create, edit and delete. Names and colours come from the vdir's own metadata files; qtmaildir depends on neither khal nor its config. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
12 daysfix: sanitise untrusted contact names on insertionDanilo M.2-23/+100
A vCard FN is untrusted and ContactStore faithfully decodes `\n` to a real newline, so `Evil\nBcc: x` was inserted raw into a recipient field and flowed through splitRecipients() to MessageBuilder. contactInsertionText() only quoted a name carrying a comma or a double quote, so every other RFC 5322 special (<, >, ;, @) and any control character reached the header unguarded. The name now has control characters and whitespace runs replaced by single spaces, and every non-empty name is quoted, with `\` escaped before `"`. Quoting contains all the specials in one step. The parser is left faithful; this is fixed at the consumer/trust boundary. Tests: a name with a decoded newline inserts no control character and yields one recipient; a name with <, >, ;, @ is quoted and yields one recipient. The three tests that expected an unquoted plain name now expect the quoted form.
12 daysdocs: document contact completion and refresh Italian translationDanilo M.3-3/+47
Translate the new config warning for contacts_dir, add the README subsection with the vdirsyncer path and the Akonadi warning, correct the query bar's from:/to: entry which had become false, and add the [Unreleased] changelog entry.
12 daysfeat: load the contact store once and feed both consumersDanilo M.2-0/+30
MainWindow reads contactsDir() once while building its UI, holds the result in m_contacts, and hands it to the query bar's completer right after that is constructed and to every ComposeWindow as it opens. An empty contactsDir() skips the call, so a machine with no address book pays nothing and warns about nothing. The directory is not watched: a restart picks up a vdirsyncer update, and a QFileSystemWatcher would be a live-index feature nobody asked for. No action is added, so none of the five places in "Adding an action is FIVE places" applies: there is no name in KeyMap::knownActions(), no default binding, no icon-table entry and no menu entry to add.
12 daysfeat: complete contacts in the query barDanilo M.3-5/+153
from: and to: now offer the vCard store's addresses, each quoted via SearchTerm::quote() with the contact's name as the description. The store is the enumerator libnotmuch does not expose, which is what the old complete-nothing comment said was missing; it keeps that role for folder:, subject:, attachment:, thread: and id:. With no store configured the branch returns {} and the behaviour is unchanged.
12 daysfeat: complete contacts in the composer recipientsDanilo M.3-5/+515
One shared QCompleter serves To, Cc and Bcc, attached with setWidget and never setCompleter, which resets the prefix to the whole field and stops matching after the first comma. The prefix is the comma-delimited token under the cursor, set by hand from textEdited; accepting replaces only that token and leaves the rest of the field alone. Candidates match the name and the address case-insensitively. A display name containing a comma is quoted on insertion, and splitRecipients() is now quote-aware so the quoted name survives as one recipient. Contacts reach the composer through setContacts() rather than a fourth constructor argument, so every existing three-argument construction and test stays as it was. An empty list leaves the fields behaving exactly as before completion existed.
12 daysfeat: add the contacts_dir config keyDanilo M.3-0/+150
Adds Config::contactsDir(), the [general] key Task 5 will read to locate the ContactStore. Empty means the feature is off. The key is read WITHOUT the general/ prefix, like notmuch_config, because QSettings' INI backend strips a section literally named [general]. Absent or empty is silent; a set path that does not exist is reported through addProblem(). A leading ~ is expanded by a local helper, since config.cpp expands no other path and this is the first one to need it.
12 daysfeat: add ContactStore, the vCard address-book parseDanilo M.14-0/+711
Reads a vdirsyncer contacts directory of vCard 3.0 files into a QList<Contact> for the completion work that follows. Pure over values, no widget and no QCompleter, so the parse is testable without a window. unfold() joins folded lines before any field is looked at; parseCard() splits property from value on the first colon outside a quoted parameter, unescapes FN, and yields one contact per EMAIL line; loadDirectory() walks recursively, skips unreadable or addressless cards, de-duplicates on the address case-insensitively, and sorts by name then address. N, PHOTO, ADR and TEL are deliberately not used. 23 new tests. No user-facing strings, so no tr() change.
12 daysdocs: add the contact completion implementation planDanilo M.2-3/+342
Item 204, six tasks, no spec: the shape was settled with the user on 2026-09-18 rather than brainstormed, so the plan carries the four decisions itself (the config key, loading once, matching on name and address, and the four vCard fields that matter). No new dependency. libical is installed but its vCard parser is 4.0 and this machine has 3.0.20, and libicalvcal is the old vCalendar converter rather than a vCard reader, so four fields of a hand parse is proportionate. The measurements are in the plan because two of them change what a correct implementation looks like. Of 117 cards in the real store only 14 carry an address, so a short candidate list is right rather than broken, and a fixture where every card completes would not be representative. The store is unfolded, so the folded-card fixture is the one the real data could never have caught. The largest trap is one this repository has already paid for twice: QLineEdit::setCompleter() is unusable for a field holding a list, and a test written with setText() passes against that bug because setText does not drive a completer at all. The plan requires typed keys and a mutation check on the second-recipient case. The query bar half comes with a correction: querycompleter.cpp:652-656 says addresses need an enumerator libnotmuch does not expose, which stops being true for from: and to: the moment the store exists. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
12 daysdocs: reconcile the backlog with the notes, and split item 72Danilo M.1-19/+270
The 2026-09-18 pass over the user's notes. One new defect, one item split into five, and one stale blocker cleared. Item 203 is new: marking a message as spam crashed the application, and the user can replicate it. The cause is NOT verified and the entry says so. The path was read without finding a null dereference, and the account the note names now carries a spam key, so the observation may predate 0.29.0's config change. It needs a reproduction with the terminal output or a backtrace before it can be worked. Item 72 held four different features in one line of the notes, which is why it sat unplannable for six weeks. The user named what they want, so it splits into 204 (recipient completion, in the composer and the query bar), 205 (editing contacts), 206 (a calendar window: its own top-level window and libical, both settled by the user) and 207 (invitations, blocked on 206). Item 208 came out of the same conversation: an "add to contacts" gesture, which writes a vcard and so lands on 205's writer rather than 204's reader. A sender's card in the message pane was offered and declined; the notes' edge-tts vocal reminders are their own project, since nothing here runs on a timer. 72 stays as the index entry the notes' single line maps to. The data those items need is on disk and was verified rather than assumed: 117 vcards and 312 events under the vdirsyncer paths, libical 3.0.20 with headers and a .pc file. Two traps are recorded because they cost a wrong answer each: the contacts vdir is not the obvious path, which belongs to Akonadi, and libical does not parse vCard at all (that is 4.0; libicalvcal is the old vCalendar converter), so contacts are a hand parse. Item 123 (send) was still marked "specified" although it shipped, so five items read as blocked on it. Both corrected. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14docs: close the backlog items shipped in 0.29.0Danilo M.2-319/+309
187 (Spam view), 190 (Mark spam on the bar + icon), 195 (Mark spam leaves unread), 197 (no way to say not spam), 201 (un-spam from the Spam view) and 202 (spam kept inbox). Sections moved to the closed file and the status rows mark them done.
2026-09-14docs: add the CLI selectors implementation planDanilo M.1-0/+2044
2026-09-14release: 0.29.0v0.29.0Danilo M.2-1/+9
2026-09-14docs: use the renamed helper names in item 202Danilo M.1-3/+3
2026-09-14fix(hooks): spam is not an inbox arrivalDanilo M.5-73/+232
notmuch's new.tags applies inbox to every newly indexed file and the Inbox filter is tag:inbox, so spam-folder mail appeared in the Inbox view. Add spam to the post-new hook's NOT_ARRIVALS set so the existing all-files carve-out covers it, and rename the sent_* helpers to not_arrival_* now that the list means more than sent mail. Trash stays out: measured 0 affected files and it is out of scope.
2026-09-14fix: keep the message bar populated on a spam replyDanilo M.3-9/+64
The spam branch of populateMessageBar() was keyed on the path predicate alone, but its only action is hidden on a reply, so a reply inside an expanded spam conversation lost Reply, Forward and Star. Skip the branch when the reply guard is set, so the ordinary branch populates instead. Extend notSpamIsOfferedInTheSpamView with the message-bar assertions the QAction-only check missed, waiting for the reply row to load first, and mark Task 9's step checkboxes done in the plan.
2026-09-14fix: the Not spam review findingsDanilo M.5-19/+145
Close the two Important test gaps and fold in the minor notes. notSpamIsAbsentOnAReplyRow passed for the wrong reason: the reply node had no filePath and the selected child was the first message, so the predicate answered false on the empty path and hid the action with or without the reply guard. Give the reply a real spam path and select the actual reply child; mutation-checked that removing the guard now fails the test. Add notSpamThreadMovesEveryMessageHome, the thread-scoped coverage notSpamThreads()/m_pendingThreadScope/wholeThreadIds had none of, and assert the folded thread-scoped trash Restore re-adds the inbox tag. Rename the label Not junk -> Not spam (no free mnemonic in the Message menu) to match the rest of the UI, with the Italian translation updated, and add a changelog line for the thread-scoped Restore inbox-tag fix.
2026-09-14feat: a Not spam actionDanilo M.9-93/+713
Backlog item 201. A message in the Spam view had no way back out: spam is one-way and Restore is hidden outside the trash. not_spam moves each message to the folder its moved-from: origin names, falling back to the account's inbox (reported) for provider-caught mail with no origin. The action is labelled "Not junk" on the free Alt+J: every letter of "Not spam" is taken in the Message menu, and Alt+P (Re&ply) and Alt+S (Mark &spam, frozen) are unavailable. restoreResolvedMessages() and the undelete_thread branch are parameterised with the cleared tag and undo description rather than copied, so Delete and Not spam cannot drift.
2026-09-14fix: gate spam like delete, and keep one origin in the modelDanilo M.6-19/+267
2026-09-14docs(i18n): correct the spam docs review findingsDanilo M.2-13/+11
2026-09-14docs(i18n): fix the spam docs review findingsDanilo M.3-15/+24
2026-09-14docs(i18n): translate the spam stringsDanilo M.4-10/+103
2026-09-14feat: find stranded spam mailDanilo M.4-0/+117