mail

package
v1.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 2, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package mail reads the places HEY keeps postings — a box, a label, a collection, a bundle's unseen threads, a contact's threads — through one Source type and one page read, so a caller never has to know which endpoint a source is served by.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func TopicIDIn

func TopicIDIn(appURL string) int64

TopicIDIn reads the thread out of a posting's URL, which is the only place HEY's posting JSON says which topic a posting is: `_posting.jbuilder` serves no topic and no topic_id, and the web app follows the URL. A URL that addresses something else — a contact page, a bundle view — has no topic and answers zero.

func TopicIDOf added in v1.0.0

func TopicIDOf(posting generated.Posting) int64

TopicIDOf resolves the thread a posting opens: its own app_url, or — for a bundle, whose app_url is its sender's contact page — its app_bundle_url. HEY points that at a topic exactly when the bundle holds one unseen thread (haystack's `bundle_posting` route), which is the thread the bundle row opens in the web app. A bundle with several unseen threads opens a bundle view of its own, and one with none opens the contact, so neither names a topic and both answer zero.

Types

type Collection

type Collection struct {
	ID   int64
	Name string
}

Collection is a collection a posting's topic belongs to.

type Contact

type Contact struct {
	ID           int64
	Name         string
	EmailAddress string
}

Contact is who a posting came from.

type Entry

type Entry struct {
	ID                    int64
	CreatedAt             time.Time
	Creator               Contact
	AlternativeSenderName string
	Summary               string
	Body                  htmlutil.Markdown
	BodyHTML              string
	// BodyState is what became of the body when the entry came through threadload:
	// hydrated, bodyless, over_limit or failed. Empty for an entry read another way.
	BodyState string
}

Entry is one message in a thread. CreatedAt is the time HEY served, not a string of it, for the reason Posting gives: a date formatted and parsed back is a date a reader east of UTC reads as yesterday's.

Body is Markdown, converted once here at the edge, and BodyHTML keeps the HTML HEY served so the image and attachment extractors still have the attributes Markdown drops.

func LoadedEntry

func LoadedEntry(loaded threadload.Entry) Entry

LoadedEntry describes an entry threadload read: the message when there is one, and what became of the body either way.

func NewEntry

func NewEntry(entry generated.Entry, message generated.Message) Entry

NewEntry describes a thread's entry against the message HEY served for it. A topic lists its entries and each message is read on its own, and the two disagree about what they carry: an entry read as part of a bundle has no creator and no timestamp of its own, and a message read directly has a subject where the entry has a summary.

type Extenzion

type Extenzion struct {
	ID   int64
	Name string
}

Extenzion is the HEY extension — a group address — a posting arrived through.

type Folder

type Folder struct {
	ID   int64
	Name string
}

Folder is a label a posting is filed under.

type Kind

type Kind string

Kind is which of HEY's posting endpoints a Source is served by.

const (
	KindBox        Kind = "box"
	KindFolder     Kind = "folder"
	KindCollection Kind = "collection"
	KindBundle     Kind = "bundle"
	KindContact    Kind = "contact"
)

type Page

type Page struct {
	Postings []generated.Posting
	Cursor   string
	Total    int
}

Page is one page of a source's postings. Cursor is whatever the next read needs to carry on where this page stopped — a geared_pagination cursor for a label or a collection, HEY's own next_history_url for a box — and is empty at the end of the list. Total is HEY's count of everything in the source, zero where it does not say.

func ReadPage

func ReadPage(ctx context.Context, client *hey.Client, source Source, cursor string) (Page, error)

ReadPage reads the page of a source that begins at cursor. An empty cursor reads the first page.

func ReadSeenPage added in v1.3.0

func ReadSeenPage(ctx context.Context, client *hey.Client, cursor string) (Page, error)

ReadSeenPage reads a page of the Imbox's Previously Seen postings, which HEY serves on their own route ordered by when they were seen — the Imbox's own pages order seen postings last, which is why the box cannot stand in for this. There is no Source parameter: the route is account-scoped and names the Imbox itself. An empty cursor reads the first page.

type Posting

type Posting struct {
	ID                    int64
	TopicID               int64
	CreatedAt             time.Time
	Name                  string
	Summary               string
	AlternativeSenderName string
	Seen                  bool
	// IsBundle marks a row that is a bundle of one contact's unseen threads. It comes
	// from the posting's kind — HEY's `bundled` flag means filed *inside* a bundle.
	IsBundle          bool
	BubbledUp         bool
	Muted             bool
	VisibleEntryCount int32
	Creator           Contact
	Extenzions        []Extenzion
	Folders           []Folder
	Collections       []Collection
}

Posting is one row of a source: a thread, a bundle or a single entry as HEY lists it. The timestamps are the ones HEY served, so whatever shows one decides how it reads — formatting a time into a string and parsing it back is how a reader east of UTC ends up looking at yesterday's date.

func NewPosting

func NewPosting(posting generated.Posting) Posting

NewPosting describes one posting HEY answered with. Its text is sanitized here, once, so that every row, picker and notice that shows a posting shows it inert.

func Postings

func Postings(postings []generated.Posting) []Posting

Postings describes a page of postings HEY answered with.

type ReplyPrefill added in v1.4.0

type ReplyPrefill struct {
	Subject        string
	ActingSenderID int64
	Addressed      ReplyRecipients
}

ReplyPrefill is how a reply starts out, as HEY prefills it: the "Re: …" subject it goes out under, the sender it goes out as, and who it goes out to. The prefill's quoted content is deliberately not carried: a reply's content is the writer's body alone — the server appends the quoted original at delivery (auto_quoting defaults on), so echoing the prefill's quote back would double it.

func ReplyPrefillFromServer added in v1.4.0

func ReplyPrefillFromServer(ctx context.Context, client *hey.Client, entryID int64) (ReplyPrefill, bool)

ReplyPrefillFromServer asks HEY how a reply to the entry starts out (GET /entries/{id}/replies/new): the "Re: …" subject the reply carries; the sender it goes out as — resolved from the entry's own to and from addresses, so a thread on a shared or alternate address answers as that address, not the account default, and named only when it differs from the acting user; and its recipients — the entry's sender moved onto the To line and the acting user's own addresses, aliases and catch-alls excluded — the exclusion no client can compute locally, and the reason a reply used to be able to CC its writer back to themselves. A false answer sends the caller to its local fallback: a failed read needs one, and so does an empty recipient list — on a thread with yourself, everyone HEY excludes is everyone there is, and the local list is what keeps that reply addressable. The subject and sender are answered even when the recipients are not — only they need the fallback, not what HEY already supplied.

type ReplyRecipients added in v1.4.0

type ReplyRecipients struct {
	To  []string
	CC  []string
	BCC []string
}

ReplyRecipients is who a reply goes out to, in HEY's three kinds of addressing.

type Source

type Source struct {
	Kind      Kind
	ID        int64
	Name      string
	BoxKind   string
	AppURL    string
	CreatedAt time.Time
	UpdatedAt time.Time
}

Source is a place postings are read from. BoxKind carries HEY's own kind for a box (hey.BoxKindImbox and friends), which is what tells the named box routes apart; it is empty for a label or a collection.

func BoxSource

func BoxSource(box *generated.BoxShowResponse) Source

BoxSource describes the box HEY answered with.

func BundleSource added in v1.4.0

func BundleSource(postingID int64, contact generated.Contact) Source

BundleSource describes a bundle posting as a source: the unseen threads it groups, named for the bundled contact. The ID is the bundle row's own box item id, which is what the unseen route is addressed by.

func CollectionSource

func CollectionSource(collection *generated.CollectionWithPostings) Source

CollectionSource describes the collection HEY answered with.

func ContactSource added in v1.4.0

func ContactSource(contact *generated.ContactDetail) Source

ContactSource describes a contact as a source: every thread they are on, seen and unseen — the list HEY heads with the contact's entries_title.

func FolderSource

func FolderSource(folder *generated.FolderWithPostings) Source

FolderSource describes the label HEY answered with.

func ListedBoxSource

func ListedBoxSource(box generated.Box) Source

ListedBoxSource describes a box from HEY's list of them, which names the boxes without serving any of their postings or URLs.

func (Source) Coverable

func (s Source) Coverable() bool

Coverable reports whether cover art belongs over this source's seen postings. Only the Imbox is coverable, which is haystack's rule in Box::Imbox#coverable?.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL