Table of Contents

Method ReadFlagChangesOnAsync

Namespace
MailFathom.Application.Mail.Mutations
Assembly
MailFathom.Application.dll

ReadFlagChangesOnAsync(MailAccountId, MailFolderResolutionId, ImapUidValidity, IReadOnlyCollection<ImapUid>, DateTimeOffset, CancellationToken)

Reads the flag and keyword stores issued against any of the occurrences a reconciliation window found moved.

Task<IReadOnlyList<MailboxMutationRecord>> ReadFlagChangesOnAsync(MailAccountId accountId, MailFolderResolutionId folderResolutionId, ImapUidValidity uidValidity, IReadOnlyCollection<ImapUid> uids, DateTimeOffset issuedAfter, CancellationToken cancellationToken)

Parameters

accountId MailAccountId

The account whose mutations are read.

folderResolutionId MailFolderResolutionId

The alias binding the occurrences were stored under.

uidValidity ImapUidValidity

The UIDVALIDITY the window was opened for.

uids IReadOnlyCollection<ImapUid>

The UIDs whose \Seen flag, \Flagged flag, or keywords the window found standing somewhere new.

issuedAfter DateTimeOffset

The earliest previous observation among those occurrences, before which no record can account for anything.

cancellationToken CancellationToken

Cancels the read.

Returns

Task<IReadOnlyList<MailboxMutationRecord>>

Every store of one of those values issued against one of those occurrences, which may be none.

Remarks

This is the read the whole provenance question turns on. A flag change reaches synchronization as a changed modification sequence, which is exactly what a person marking mail read or starring it in their own client produces, so nothing in the server's answer distinguishes the two and only the record does. A rule conditioned on unread mail that marks mail read would otherwise re-evaluate every message it had just acted on.

All five stores are read together rather than one query per value, because one FLAGS response carries every value at once: an occurrence whose star and whose label both moved is one question, and splitting it would cost a query per value on every window that found anything. Which record answers for which value is settled by the caller against the record's own comparisons.

Only the occurrences where something actually moved are asked about, so a window that found the mailbox unchanged — which is most windows — asks nothing. The idempotency identity no longer bounds what one occurrence can carry: the requester of a caller-authored change is the invocation, so an agent that stars and unstars one message leaves a record per call and nothing ever deletes one. What bounds the answer instead is stated rather than assumed — the UIDs, issuedAfter, and the stage. Each drops only records that could account for nothing, because all three comparisons below require a record past Recorded whose stage moved after the occurrence was last read; a change written down and not yet issued explains no reading, and against a freshly triaged occurrence those are the newest rows in the table. What survives is then capped at MaximumFlagChangeRecordsPerValue for each occurrence's each value.

Every surviving record is returned, spent or not, because whether one still accounts for anything is settled against the occurrence's own last observation rather than against a mark on the row. That comparison belongs to AccountsForSeenStateOf(EmailOccurrenceId, bool, DateTimeOffset), AccountsForFlaggedStateOf(EmailOccurrenceId, bool, DateTimeOffset), and AccountsForKeywordsOf(EmailOccurrenceId, RemoteEmailKeywords, RemoteEmailKeywords, DateTimeOffset), which the caller applies to what this returns.

Exceptions

ArgumentNullException

Thrown when uids is null.