Skip to content

Address book: find mail from or to a contact (#370)#373

Merged
kellylford merged 5 commits into
mainfrom
feature/370-find-mail-for-contact
Jul 26, 2026
Merged

Address book: find mail from or to a contact (#370)#373
kellylford merged 5 commits into
mainfrom
feature/370-find-mail-for-contact

Conversation

@kellylford

Copy link
Copy Markdown
Owner

Fixes #370.

What it does

Select a contact in the address book, press Shift+F10, and choose Find mail from this contact or Find mail to this contact. The address book closes, the message list fills with the matches (newest first), focus lands on the message list, and the count is announced — "12 messages from Bob Baker." The window title reads Mail from Bob Baker so the results are distinguishable from a folder.

Both actions are also registered in the address book's own CommandRegistry, so they appear in its palette (Ctrl+Shift+P) as Find Mail From Contact / Find Mail To Contact and can be given a shortcut. No default key is claimed.

How it works

  • Results are a virtual folder, the same mechanism All Flagged uses. The sentinel \u0000ContactMail:{from|to}|{escaped address} carries direction and address, so FetchVirtualAsync, refresh, sync-range changes, and the OnFolderSynced merge all route back through the same search instead of leaving a stale list behind.
  • Matching is a case-insensitive substring test against the From or To header — both hold display name plus address, and To holds a list.
  • Source of messages is the local cache (LoadAllSummariesAsync), i.e. every account and folder, subject to the sync range. --online mode has no cache, so it sweeps the non-excluded folders directly, mirroring FetchAllFlaggedAsync.
  • Entry point MainViewModel.ShowContactMailAsync delegates to SelectFolderAsync, so filter / search / saved-view state is reset exactly as it is when navigating to any other folder.

Modal-dialog safety

The address book is shown with ShowDialog() over a window with a live WebView2. Running the search while that nested message loop is alive is the crash pattern CLAUDE.md documents, so the context-menu action only records the request and closes the address book; the search runs in OpenAddressBook after ShowDialog() returns.

Scope

  • The two items appear only when the host supplied search actions — the address book opened from a compose window (Ctrl+Shift+B while composing) has no message list to show results in, so they are hidden there and the palette entries are unavailable.
  • A contact with no email address can't be searched for; CanFindMailForContact gates both the menu items and the palette commands.
  • The contact list's ContextMenu automation name changed from "Add to group" to "Contact actions" — it is no longer only about groups. Group items are unchanged, below a separator.

Tests

QuickMail.Tests/ContactMailSearchTests.cs (12 tests): from/to filtering, newest-first order, case-insensitive match, title with and without a display name, empty-result message, blank address no-op, filter/search reset, sentinel round-trip through SelectFolderCommand (including an address that percent-escapes), and the address-book VM side (hidden without search actions, commands report the selected contact, no-address contact is not searchable, CanFindMailForContact change notification).

Full suite: 1582 passed.

Docs

User guide gets a "Finding Mail From or To a Contact" subsection; release-notes entry in docs/release-notes-v0.8.37.md.

The address book had no way to get from a person to their mail. Select a
contact, press Shift+F10, and choose "Find mail from this contact" or
"Find mail to this contact": the address book closes and the message list
fills with the matches, newest first.

The results are a virtual folder, the same mechanism All Flagged uses. Its
sentinel carries the direction and the percent-escaped address, so refresh,
a sync-range change, and the folder-synced merge all route back through the
same search rather than dropping the user into a stale list. Matching is a
case-insensitive substring test against the From or To header, over every
message in the local cache across all accounts and folders; --online mode
sweeps the folders directly instead.

The search cannot run while the address book is open: mutating this
window's UI from inside a modal dialog's nested message loop is exactly the
pattern that crashes the app (see CLAUDE.md). So the action records the
request and closes the address book, and the search runs after ShowDialog
returns. Focus then moves to the message list and the count is announced as
a Result.

The two actions are hidden when the address book is opened from a compose
window, which has no message list to show results in. They are also
registered in the address book's command registry, so they appear in its
palette and can be given a shortcut in Settings.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A plain substring test reported a match for bob@example.com in both
notbob@example.com and bob@example.com.au. Require each hit to sit on an
address boundary — anything but an address character on either side, which
is what the surrounding angle brackets, quotes, commas, and spaces in a
header supply.

Also note in the user guide that "find mail to" matches the To line, so a
message where the contact was only in Cc does not appear (the cached
message summary has no Cc field).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
kellylford and others added 2 commits July 26, 2026 08:31
…e's fire-and-forget convention

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@kellylford
kellylford merged commit ced93ed into main Jul 26, 2026
4 checks passed
@kellylford
kellylford deleted the feature/370-find-mail-for-contact branch July 26, 2026 13:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add the ability to search for mail to and from a contact from the address book

1 participant