Help us shape the future of Go! Take the 2026 Go Developer Survey

catalog

package
v0.135.0 Latest Latest
Warning

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

Go to latest
Published: Oct 28, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package catalog is ContentKit's own content: content items (a gallery, a movie, a creator or blog post), their per-language text, creators, versions with their text and segments, the state machine (draft, published, scheduled, deleted), the listing projection (content_facets), the one visibility rule every read applies, the retained event log and the catalog totals. Kinds are configuration (package kinds); nothing here names a host's nouns.

Index

Constants

View Source
const DefaultDeletedRetention = 30 * 24 * time.Hour

DefaultDeletedRetention is how long deleted content keeps its media and can be restored before PurgeDeleted erases it.

View Source
const DefaultEventRetention = 90 * 24 * time.Hour

DefaultEventRetention is independent of readers' progress.

View Source
const (
	EventAudienceReleased = "version.audience_released"
)
View Source
const (
	EventContentCreated = "content.created"
)
View Source
const (
	EventContentDeleted = "content.deleted"
)
View Source
const (
	EventContentPublished = "content.published"
)
View Source
const (
	EventContentPurged = "content.purged"
)
View Source
const (
	EventContentRestored = "content.restored"
)
View Source
const (
	EventContentScheduled = "content.scheduled"
)
View Source
const (
	EventContentUnpublished = "content.unpublished"
)
View Source
const (
	EventContentUpdated = "content.updated"
)
View Source
const (
	EventPublicationCompleted = "version.publication_completed"
)
View Source
const (
	EventVersionCreated = "version.created"
)
View Source
const (
	EventVersionDeleted = "version.deleted"
)
View Source
const (
	EventVersionPublished = "version.published"
)
View Source
const (
	EventVersionPurged = "version.purged"
)
View Source
const (
	EventVersionUpdated = "version.updated"
)
View Source
const ImageScheme = "contentkit:"

ImageScheme is the URL scheme of an inline image reference in a body.

View Source
const MaxManageIDs = 100

MaxManageIDs bounds ManageQuery.IDs.

View Source
const MaxVersionStatuses = 100

MaxVersionStatuses bounds VersionStatuses.

View Source
const Queue = "contentkit_catalog"

Queue is the legacy base name. Runtime workers use QueueFor(catalog).

View Source
const (
	StateDeleted = "deleted"
)
View Source
const (
	StateDraft = "draft"
)
View Source
const (
	StatePublished = "published"
)

Variables

This section is empty.

Functions

This section is empty.

Types

type AuthError

type AuthError struct{ Err error }

AuthError is a permission check the auth could not answer: 401 when the credential ended, else 503. It never grants.

func (*AuthError) Error

func (e *AuthError) Error() string

func (*AuthError) Unwrap

func (e *AuthError) Unwrap() error

type BodyImageInput added in v0.87.0

type BodyImageInput struct {
	Image   *string `json:"image"`
	Version string  `json:"version"`
}

BodyImageInput names a body image uploaded to one version (Upload.Body).

type Content

type Content struct {
	ID   string `json:"id"`
	Kind string `json:"kind"`
	// Code and Path are its page: /{lang}/{route}/{code}/{slug}.
	Code string `json:"code"`
	Path string `json:"path"`
	// Language is the localized text language. Title may instead come from the
	// selected edition or OriginalTitle under the kind's title policy.
	Language string `json:"language"`
	Title    string `json:"title"`
	// OriginalTitle and OriginalLanguage project the selected version only.
	OriginalTitle    string `json:"original_title,omitempty"`
	OriginalLanguage string `json:"original_language,omitempty"`
	// Slug is Path's decorative tail in the page's language (entity and
	// title, "/"-joined); Slugs is it per language. Links built from them
	// equal Path.
	Slug        string            `json:"slug,omitempty"`
	Slugs       map[string]string `json:"slugs,omitempty"`
	Description string            `json:"description,omitempty"`
	// Body shows each inline image with its selected edition's signed URL;
	// only editors and viewers Decision allows get it. Description is the
	// teaser everyone reads.
	Body string `json:"body,omitempty"`
	// Decision is the viewer's access to the item: allowed when they open any
	// of its versions, else its first version's paywall.
	Decision Decision `json:"decision"`
	// Images maps each resolved image name of Body to its URL: the body
	// images of the edition the viewer opens (DefaultVersion among them).
	Images map[string]string `json:"images,omitempty"`
	// Cover is the cover of the edition the request's language opens by
	// default (DefaultVersion): what cards and meta tags show.
	Cover *Cover `json:"cover,omitempty"`
	State string `json:"state"`
	// Comments is open, closed or hidden.
	Comments     string         `json:"comments"`
	PublishAt    *time.Time     `json:"publish_at,omitempty"`
	PublishedAt  *time.Time     `json:"published_at,omitempty"`
	Live         bool           `json:"live"`
	Access       access.Rule    `json:"access"`
	Restrictions []string       `json:"restrictions"`
	Attributes   map[string]any `json:"attributes"`
	ChannelID    string         `json:"channel_id,omitempty"`
	// Channel identifies the original's primary authoring channel while active.
	Channel  *channels.Card `json:"channel,omitempty"`
	Creators []Creator      `json:"creators"`
	// Entities are shared/null-version memberships. Version-specific entities
	// are carried by each Version.Entities, never merged across editions here.
	Entities []EntitySummary `json:"entities"`
	Versions []Version       `json:"versions"`
	// SelectedVersionID is the edition selected for this response: the direct
	// version lookup's edition, or the default among the visible versions.
	SelectedVersionID string `json:"selected_version_id,omitempty"`
	// Viewer is the signed-in reader's standing with the item, to resume
	// it; each version carries its own.
	Viewer *ViewerState `json:"viewer,omitempty"`
	// Editor: the viewer may edit it; the fields below are theirs.
	Editor    bool       `json:"editor"`
	Revision  int64      `json:"revision"`
	CreatedAt time.Time  `json:"created_at"`
	UpdatedAt time.Time  `json:"updated_at"`
	DeletedAt *time.Time `json:"deleted_at,omitempty"`
}

Content is a resolved reader response. Text and media fields are read-only projections of SelectedVersionID; the parent stores no text. It includes live versions (every version for editors), entities and attributes. A viewer who opens none of its versions gets what its kind's locked view shows (kinds.Locked), and of each locked version what that view shows.

type ContentInput

type ContentInput struct {
	Kind string `json:"kind"`
	// Comments is open (the default when permitted), closed (existing ones
	// show, new ones are refused) or hidden. A kind excluding open defaults to
	// its first configured comment mode.
	Comments string `json:"comments,omitempty"`
	// Access defaults to public when permitted, otherwise the kind's first
	// allowed level (and the first configured tier for tier access).
	Access       *access.Rule   `json:"access,omitempty"`
	Restrictions []string       `json:"restrictions,omitempty"`
	Attributes   map[string]any `json:"attributes,omitempty"`
	// ChannelID is the primary channel; its current editors govern the item.
	// It never changes.
	ChannelID string `json:"channel_id,omitempty"`
	// Creators nil credits the creating person as its author.
	Creators *[]Creator `json:"creators,omitempty"`
	// Versions defines initial editions. Omission creates one empty edition.
	// A kind with hidden versions accepts at most one initial edition.
	Versions []VersionInput `json:"versions,omitempty"`
}

ContentInput creates a content item, a draft until published.

type ContentPatch

type ContentPatch struct {
	Comments     *string        `json:"comments,omitempty"`
	Access       *access.Rule   `json:"access,omitempty"`
	Restrictions *[]string      `json:"restrictions,omitempty"`
	Attributes   map[string]any `json:"attributes,omitempty"` // replaces all
	Creators     *[]Creator     `json:"creators,omitempty"`
	// Revision, when set, refuses the edit (409) unless it is current.
	Revision *int64 `json:"revision,omitempty"`
}

ContentPatch edits a content item; nil fields stay.

type Cover

type Cover struct {
	Image      string                  `json:"image,omitempty"`
	URL        *string                 `json:"url,omitempty"`
	Renditions []media.PublicRendition `json:"renditions,omitempty"`
}

Cover is a version's cover: Image is the cover upload its editors selected ("i-{uuid}"; empty shows its first, else its kind's default); URL is its widest public rendition.

type Creator

type Creator struct {
	UserID   string `json:"user_id"`
	Role     string `json:"role,omitempty"`
	Position int    `json:"position"`
}

Creator is a person credited on a content item. Credits do not grant access or editing authority; those follow the authenticated author and channel roles.

type CreatorsInput

type CreatorsInput struct {
	Creators []Creator `json:"creators"`
}

CreatorsInput replaces the creators.

type Decision

type Decision struct {
	Allowed bool `json:"allowed"`
	// Unknown: entitlements could not be read; denied, nothing offered.
	Unknown bool `json:"unknown,omitempty"`
	// Sell is the keys a paywall offers; Requires the key a buyer holds first.
	Sell     []string `json:"sell,omitempty"`
	Requires string   `json:"requires,omitempty"`
	// Preview is what a locked viewer still reads; Releases describe additional audiences.
	Preview  *access.Preview  `json:"preview,omitempty"`
	Releases []access.Release `json:"releases,omitempty"`
}

Decision is a viewer's access verdict on a version.

type EntitySummary added in v0.117.0

type EntitySummary struct {
	ID   string `json:"id"`
	Kind string `json:"kind"`
	Slug string `json:"slug"`
	Name string `json:"name"`
	Code string `json:"code,omitempty"`
	Path string `json:"path,omitempty"`
}

EntitySummary names one entity a content item belongs to; Code and Path are its page, as content's are, when its kind has one.

type Event

type Event struct {
	ChannelID string
	Type      string
	// Kind is the content's kind, or an entity event's entity kind.
	Kind      string
	ContentID string
	VersionID string
	// EntityID is an entity event's subject; MergedInto its survivor.
	EntityID      string
	MergedInto    string
	OccurredAt    time.Time
	Release       *access.Release
	PublicationID string
	ChapterIDs    []string
	Released      *bool
}

Event is the catalog fact appended by a mutation. Readers receive EventRecord.

type EventPage added in v0.116.0

type EventPage struct {
	Events           []EventRecord `json:"events"`
	NextCursor       string        `json:"next_cursor"`
	HasMore          bool          `json:"has_more"`
	RetentionSeconds int64         `json:"retention_seconds"`
}

EventPage always provides a continuation, including an empty stream.

type EventQuery added in v0.116.0

type EventQuery struct {
	After string
	Limit int
	Types []string
}

EventQuery reads one app's retained log. Callers own their continuation.

type EventRecord added in v0.116.0

type EventRecord struct {
	ID        string `json:"id"`
	Version   int    `json:"version"`
	Type      string `json:"type"`
	Kind      string `json:"kind"`
	ContentID string `json:"content_id"`
	VersionID string `json:"version_id,omitempty"`
	// EntityID is an entity event's subject; MergedInto its survivor.
	EntityID   string          `json:"entity_id,omitempty"`
	MergedInto string          `json:"merged_into,omitempty"`
	OccurredAt time.Time       `json:"occurred_at"`
	RecordedAt time.Time       `json:"recorded_at"`
	Source     string          `json:"source"`
	Scope      string          `json:"scope"`
	Data       json.RawMessage `json:"data,omitempty"`
}

EventRecord is an immutable committed catalog fact. IDs are decimal strings so JSON clients do not lose bigint precision.

type ImportContent

type ImportContent struct {
	ID          string
	Kind        string
	State       string
	PublishAt   *time.Time
	PublishedAt *time.Time
	DeletedAt   *time.Time
	// Comments is open (when empty), closed or hidden.
	Comments     string
	Access       *access.Rule
	Restrictions []string
	Attributes   map[string]any
	ChannelID    string
	Creators     []Creator
	Versions     []ImportVersion
	Code         string
	CreatedAt    *time.Time
	UpdatedAt    *time.Time
}

ImportContent is a content item as the bulk import writes it: its id, state and times kept, trusted (not screened). Nil Creators, Text and a version's Text keep what is stored; given ones replace it. Chapters are authored through PutVersions after content and source media are imported.

type ImportVersion

type ImportVersion struct {
	ID string
	// Code is the version code to keep; empty keeps the stored one or
	// assigns one. A version never changes its code.
	Code        string
	Languages   []string
	Traits      []string
	Label       string
	State       string
	PublishAt   *time.Time
	PublishedAt *time.Time
	DeletedAt   *time.Time
	Access      *access.Rule
	Attributes  map[string]any
	Ordinal     *int
	// Cover selects one of the version's cover uploads (i-{uuid}).
	Cover            string
	OriginalTitle    *string
	OriginalLanguage *string
	Text             map[string]VersionText
	// Segments must be nil. Use PutVersions after importing source media so
	// chapter changes hold the recovered manifest lock through commit.
	Segments  []Segment
	CreatedAt *time.Time
	UpdatedAt *time.Time
}

ImportVersion is one version of an ImportContent, positioned as listed.

type InlineImage

type InlineImage struct {
	Ref string  `json:"ref"`
	URL *string `json:"url"`
}

InlineImage is an inline image of a body: Ref goes where its URL would ("contentkit:i-{uuid}"); URL shows it to the editor now (null until it has a file to show).

type LanguageTotal

type LanguageTotal struct {
	Public int64            `json:"public"`
	Tiers  map[string]int64 `json:"tiers"`
}

LanguageTotal sizes one kind in one language, as Total does.

type ManageCount added in v0.104.0

type ManageCount struct {
	Total int `json:"total"`
	// Truncated makes Total a lower bound.
	Truncated bool `json:"truncated,omitempty"`
}

ManageCount is how many items a manage listing selects.

type ManageQuery

type ManageQuery struct {
	Kind string
	// State is draft, scheduled, published (live) or deleted; "" is every
	// state but deleted.
	State   string
	Channel string
	// Text matches title, description or body, ignoring case.
	Text string
	// Code selects the item with this content code; with State "" it matches
	// deleted items too, so editor links keep resolving.
	Code string
	// IDs selects these items (at most MaxManageIDs); with State "" it
	// matches deleted items too, so member lists resolve every member.
	IDs []string
	// Access keeps items whose own rule has this level; Tier those whose rule
	// names this configured tier (implying the tier level).
	Access, Tier  string
	Limit, Offset int
}

ManageQuery selects content items to manage.

type ManagementName added in v0.104.0

type ManagementName struct{ ID, Kind, Title, Language string }

ManagementName is the current name of an item its caller may edit. No body, media, public-listing policy or audience billing check is needed to navigate.

type ManagementNames added in v0.104.0

type ManagementNames struct {
	Contents []ManagementName
	Entities []entities.EntityCard
}

type ManagementSearchQuery added in v0.104.0

type ManagementSearchQuery struct {
	Query, Language                  string
	ContentKinds, EntityKinds        []string
	IncludeContents, IncludeEntities bool
	Limit                            int
}

type OrderInput

type OrderInput struct {
	IDs []string `json:"ids"`
}

OrderInput orders versions.

type PublicationStatus added in v0.128.0

type PublicationStatus struct {
	ID          string     `json:"id"`
	State       string     `json:"state"`
	ScheduledAt time.Time  `json:"scheduled_at"`
	CompletedAt *time.Time `json:"completed_at,omitempty"`
	Error       string     `json:"error,omitempty"`
}

PublicationStatus is the latest accepted edition publication operation. Its state describes work; Version.Live describes current reader availability.

type Publish

type Publish struct {
	ContentID string
	At        *time.Time
}

Publish publishes a content item now, or at At when it is in the future (scheduled).

type PublishInput

type PublishInput struct {
	PublishAt *time.Time `json:"publish_at,omitempty"`
}

PublishInput publishes now, or at PublishAt when it is in the future.

type Reader

type Reader interface {
	Read(ctx context.Context, ref contentref.ContentRef, actor access.Actor, o media.ReadOptions) (*media.ReadResult, error)
	// NamedURLs signs an opened item's named images that have no public files.
	NamedURLs(ctx context.Context, ref contentref.ContentRef, actor access.Actor, names []string) (map[string]string, error)
}

Reader reads an item as the media read API does.

type Sanitizer

type Sanitizer = content.ContentProcessor

Sanitizer cleans rich text on write: content.ContentProcessor.

type Segment

type Segment struct {
	ID         string     `json:"id"`
	Position   int        `json:"position"`
	Title      string     `json:"title,omitempty"`
	StartPage  string     `json:"start_page,omitempty"`
	Start      int        `json:"start"`
	End        int        `json:"end"`
	ReleaseAt  *time.Time `json:"release_at,omitempty"`
	ReleasedAt *time.Time `json:"released_at,omitempty"`
	Released   bool       `json:"released"`
}

Segment is a chapter of one version. StartPage names its first file in manifest order; the next chapter's marker or the end of the manifest ends it. ReleaseAt is intent. ReleasedAt is set only after durable publication.

type SegmentInput added in v0.106.0

type SegmentInput struct {
	ID        string     `json:"id,omitempty"`
	Position  int        `json:"position,omitempty"`
	Title     string     `json:"title,omitempty"`
	StartPage string     `json:"start_page,omitempty"`
	Start     int        `json:"-"` // rejected legacy Go input; never a wire field
	End       int        `json:"-"` // rejected legacy Go input; never a wire field
	ReleaseAt *time.Time `json:"release_at,omitempty"`
	Released  bool       `json:"released,omitempty"` // ignored, retained for older clients
}

SegmentInput authors a chapter by its first virtual filename in the manifest. ID preserves the chapter across title, ordering and marker edits. Numeric range authoring is rejected; Start and End exist only on displayed chapters and the separate trusted import model.

type SegmentsInput

type SegmentsInput struct {
	Segments []SegmentInput `json:"segments"`
}

SegmentsInput replaces a version's segments.

type Standing added in v0.85.0

type Standing interface {
	Standing(ctx context.Context, actor access.Actor, refs []contentref.ContentRef) ([]ViewerState, error)
}

Standing reads a signed-in person's standing with content items and versions, per ref in order: *discovery.Service.

type Total

type Total struct {
	Public    int64                    `json:"public"`
	Tiers     map[string]int64         `json:"tiers"`
	Languages map[string]LanguageTotal `json:"languages"`
}

Total sizes one kind. Public counts what an anonymous visitor's listing shows and opens; Tiers[t] every eligible item a holder of t opens, members-listed ones included. An item counts once in the kind's total and once per language any of its eligible versions serves, never per version. The site's scope and the default content filters apply.

type Totals

type Totals struct {
	Kinds map[string]Total `json:"kinds"`
}

Totals is the catalog's size by content kind.

type TotalsQuery

type TotalsQuery struct {
	// Kinds are content kinds; empty means all of them.
	Kinds []string
	// Languages are the languages Total.Languages reports; empty means every
	// language with a counted item. Kind totals ignore it.
	Languages []string
	// Domain is the stable domain name whose scope applies; empty selects
	// main. The HTTP route takes it from the request's host.
	Domain string
}

TotalsQuery selects the kinds and languages to total.

type Update

type Update struct {
	ID string
	ContentPatch
}

Update is one ContentPatch.

type Version

type Version struct {
	ID string `json:"id"`
	// Code selects it on its content's page (?v=): unique within the content,
	// fixed for the version's life.
	Code      string   `json:"code"`
	Position  int      `json:"position"`
	Languages []string `json:"languages"`
	Traits    []string `json:"traits"`
	Label     string   `json:"label,omitempty"`
	// Resolved text is selected from this version only in the request language.
	Title            string            `json:"title,omitempty"`
	Description      string            `json:"description,omitempty"`
	Language         string            `json:"language,omitempty"`
	Slug             string            `json:"slug,omitempty"`
	Body             string            `json:"body,omitempty"`
	Images           map[string]string `json:"images,omitempty"`
	OriginalTitle    string            `json:"original_title,omitempty"`
	OriginalLanguage string            `json:"original_language,omitempty"`
	// State is the editors' to see.
	State       string     `json:"state,omitempty"`
	PublishAt   *time.Time `json:"publish_at,omitempty"`
	PublishedAt *time.Time `json:"published_at,omitempty"`
	// Publication is editor-only durable work status. A pending update does
	// not replace the edition's already released reader state.
	Publication *PublicationStatus `json:"publication,omitempty"`
	// Live reports a completed release under the current publication state.
	Live bool `json:"live"`
	// Access is the version's own rule. Noneditors do not receive this field.
	Access     *access.Rule   `json:"access,omitempty"`
	Attributes map[string]any `json:"attributes"`
	// Entities are entities attached specifically to this version. Combine them
	// with Content.Entities when presenting the selected version's credits.
	Entities   []EntitySummary `json:"entities"`
	Pages      *int            `json:"pages,omitempty"`
	DurationMS *int64          `json:"duration_ms,omitempty"`
	Width      *int            `json:"width,omitempty"`
	Height     *int            `json:"height,omitempty"`
	HasAudio   bool            `json:"has_audio"`
	Ordinal    *int            `json:"ordinal,omitempty"`
	// Cover is absent when its kind's locked view hides it from the viewer.
	Cover    *Cover    `json:"cover,omitempty"`
	Segments []Segment `json:"segments"`
	Decision Decision  `json:"decision"`
	// Text is every authored language of this edition, for editors.
	Text map[string]VersionText `json:"text,omitempty"`
	// Viewer is the signed-in reader's standing with the version.
	Viewer *ViewerState `json:"viewer,omitempty"`
}

Version is what a viewer opens.

type VersionInput

type VersionInput struct {
	Languages []string  `json:"languages,omitempty"`
	Traits    *[]string `json:"traits,omitempty"`
	Label     *string   `json:"label,omitempty"`
	// State is draft or published intent. A version becomes live only when its
	// durable publication completes. A version created with its gallery
	// defaults to published intent, one added later to draft.
	State     string     `json:"state,omitempty"`
	PublishAt *time.Time `json:"publish_at,omitempty"`
	// Access is this edition's rule. ClearAccess copies the current gallery
	// creation default once; subsequent gallery edits never change it.
	Access      *access.Rule           `json:"access,omitempty"`
	ClearAccess bool                   `json:"clear_access,omitempty"`
	Attributes  map[string]any         `json:"attributes,omitempty"` // replaces all
	Text        map[string]VersionText `json:"text,omitempty"`       // replaces all
	Segments    *[]SegmentInput        `json:"segments,omitempty"`   // replaces all
	Ordinal     *int                   `json:"ordinal,omitempty"`
	// Cover selects one of the version's cover uploads by name (i-{uuid});
	// "" returns to its first.
	Cover            *string `json:"cover,omitempty"`
	OriginalTitle    *string `json:"original_title,omitempty"`
	OriginalLanguage *string `json:"original_language,omitempty"`
}

VersionInput creates or edits a version; nil fields stay (or default).

type VersionKey

type VersionKey struct{ ContentID, VersionID string }

VersionKey names one version.

type VersionPut

type VersionPut struct {
	ContentID string
	VersionID string
	VersionInput
}

VersionPut creates (VersionID "") or edits a version.

type VersionStatus added in v0.135.0

type VersionStatus struct {
	VersionID string   `json:"version_id"`
	ContentID string   `json:"content_id"`
	Kind      string   `json:"kind"`
	Languages []string `json:"languages"`
	// Deleted is the edition's or its content's deletion.
	Deleted bool `json:"deleted"`
	// Live is released and not deleted at the time of the read.
	Live bool `json:"live"`
	// LiveFrom is when its release completed, kept through deletion; absent
	// before release.
	LiveFrom *time.Time `json:"live_from,omitempty"`
}

VersionStatus is an edition's catalog state, for hosts that keep references to editions. A purged edition has none.

type VersionText

type VersionText struct {
	Title       string   `json:"title"`
	Slug        string   `json:"slug,omitempty"`
	Description string   `json:"description,omitempty"`
	Body        string   `json:"body,omitempty"`
	SortKey     string   `json:"sort_key,omitempty"`
	Aliases     []string `json:"aliases,omitempty"`
}

VersionText is one edition's authored text in one language. Inline images resolve only against this edition's media; no sibling or parent fallback.

type ViewerOptions

type ViewerOptions struct {
	Actor access.Actor
	// Host is the request's host; it picks the site (kinds.Config.DomainFor).
	Host string
	// Language, when set, lists only content with a live version serving it.
	Language string
	// Mode defaults to access.Lock.
	Mode access.Mode
	// EditorEntities are owner entities (channels) whose editors see their whole
	// content in lists; the viewer's editorship of each is checked.
	EditorEntities []string
	// Requested are entities a list requires (an entity's page): the viewer's
	// content filters do not hide them, so the page shows what the entity's
	// count counts.
	Requested []string
}

ViewerOptions describes one request's reader.

type ViewerState added in v0.85.0

type ViewerState struct {
	Favorited bool `json:"favorited"`
	// Reaction is their like (1) or dislike (-1) of the item; absent for neither.
	Reaction int `json:"reaction,omitempty"`
	// Seen, Completed, Progress, ProgressMax and Resume (the client's own
	// pointer, such as a page) come from the signal plane. Progress counts
	// whole pages for a reader and seconds for a player.
	Seen        bool       `json:"seen"`
	Completed   bool       `json:"completed"`
	Progress    uint32     `json:"progress"`
	ProgressMax uint32     `json:"progress_max"`
	Resume      string     `json:"resume,omitempty"`
	LastReadAt  *time.Time `json:"last_read_at,omitempty"`
}

ViewerState is a signed-in reader's own standing with a content item or version: their favorite and, with a signal plane, where they are in it.

Jump to

Keyboard shortcuts

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