feat(protocols): conversations - thread_id and in_reply_to written at delivery, list and read APIs
A reply now joins the conversation of the message it answers (Message-ID,
In-Reply-To, References), falling back to the subject for replies with no
headers. /mail/threads lists a folder as conversations and /mail/thread/{id}
returns one. Replying from the webmail flags the answered message.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ff89e9dc44
commit
67d0c8aa5a
17 files changed
+1025
-9
No files matched your search
@@ -0,0 +1,68 @@
|
||||
package port
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// WebmailParticipant is someone who wrote in a conversation.
|
||||
type WebmailParticipant struct {
|
||||
Name string `json:"name"`
|
||||
Address string `json:"address"`
|
||||
// Me marks the mailbox owner, so the list can say "me" instead of a name
|
||||
// the reader knows perfectly well.
|
||||
Me bool `json:"me"`
|
||||
}
|
||||
|
||||
// WebmailThread is one row of the conversation list: the newest message in the
|
||||
// folder being looked at, with what the rest of the conversation adds.
|
||||
type WebmailThread struct {
|
||||
ThreadID string `json:"thread_id"`
|
||||
UID uint32 `json:"uid"`
|
||||
Subject string `json:"subject"`
|
||||
SenderAddress string `json:"sender_address"`
|
||||
FromName string `json:"from_display_name"`
|
||||
Snippet string `json:"snippet"`
|
||||
ReceivedAt time.Time `json:"received_at"`
|
||||
HasAttachments bool `json:"has_attachments"`
|
||||
MessageCount int `json:"message_count"`
|
||||
UnseenCount int `json:"unseen_count"`
|
||||
Seen bool `json:"seen"`
|
||||
Flagged bool `json:"flagged"`
|
||||
Answered bool `json:"answered"`
|
||||
Participants []WebmailParticipant `json:"participants"`
|
||||
}
|
||||
|
||||
// WebmailThreadMessage is one message inside a conversation.
|
||||
type WebmailThreadMessage struct {
|
||||
UID uint32 `json:"uid"`
|
||||
Folder string `json:"folder"`
|
||||
Subject string `json:"subject"`
|
||||
SenderAddress string `json:"sender_address"`
|
||||
FromName string `json:"from_display_name"`
|
||||
Snippet string `json:"snippet"`
|
||||
ReceivedAt time.Time `json:"received_at"`
|
||||
HasAttachments bool `json:"has_attachments"`
|
||||
Seen bool `json:"seen"`
|
||||
Flagged bool `json:"flagged"`
|
||||
// Answered is true when the mailbox owner replied, whether the \Answered
|
||||
// flag was set or the reply is simply there in the conversation - a reply
|
||||
// written in another mail program leaves no flag, only the message.
|
||||
Answered bool `json:"answered"`
|
||||
// Own marks a message the mailbox owner wrote.
|
||||
Own bool `json:"own"`
|
||||
}
|
||||
|
||||
// ConversationReader serves the conversation views.
|
||||
type ConversationReader interface {
|
||||
// ListThreads returns the conversations that have a message in the folder,
|
||||
// newest first by that message.
|
||||
ListThreads(ctx context.Context, mailboxID, folderName, search string, limit, offset int) ([]WebmailThread, error)
|
||||
// ThreadMessages returns every message of one conversation, oldest first.
|
||||
// folderName is the folder being viewed: a conversation's messages in Trash
|
||||
// and Junk are left out unless that is the folder.
|
||||
ThreadMessages(ctx context.Context, mailboxID, folderName, threadID string) ([]WebmailThreadMessage, error)
|
||||
// MarkAnsweredByMessageID sets \Answered on the message a reply was written
|
||||
// to, wherever in the mailbox it is filed.
|
||||
MarkAnsweredByMessageID(ctx context.Context, mailboxID, messageID string) error
|
||||
}
|
||||
@@ -22,6 +22,10 @@ type PersistInboundMessageInput struct {
|
||||
FromDisplayName string
|
||||
MessageIDHeader string
|
||||
HasAttachments bool
|
||||
// InReplyTo and References place the message in its conversation. Both
|
||||
// hold Message-IDs without angle brackets.
|
||||
InReplyTo string
|
||||
References []string
|
||||
// AlreadySeen marks a copy the user themselves composed - their own Sent
|
||||
// copy, or a draft. It is not unread mail, and counting it as such is why
|
||||
// the Sent folder carried an unread badge that nothing could clear: no
|
||||
|
||||
Reference in new issue
Block a user