Documentation
¶
Overview ¶
Package issues holds the Githome web front's issues handlers: the index with its search-and-filter bar, the detail page with its comment timeline and sidebar, the comment composer, the reactions, and the new-issue form. Each handler resolves and authorizes through the same domain.IssueService the REST and GraphQL surfaces use, so the page and the API never disagree, maps the result into a fe/view model with every URL precomputed through fe/route, and renders through fe/render. A repository the viewer cannot see was turned into a hard 404 by the Resolve middleware before any handler ran (the 404-not-403 rule), and every mutation has a plain HTML form path that works with no JavaScript. See implementation/08.
Index ¶
- type Deps
- type Handlers
- func (h *Handlers) Create(c *mizu.Ctx) error
- func (h *Handlers) CreateComment(c *mizu.Ctx) error
- func (h *Handlers) DeleteComment(c *mizu.Ctx) error
- func (h *Handlers) EditComment(c *mizu.Ctx) error
- func (h *Handlers) EditSidebar(c *mizu.Ctx) error
- func (h *Handlers) EditTitle(c *mizu.Ctx) error
- func (h *Handlers) Index(c *mizu.Ctx) error
- func (h *Handlers) Labels(c *mizu.Ctx) error
- func (h *Handlers) Milestone(c *mizu.Ctx) error
- func (h *Handlers) Milestones(c *mizu.Ctx) error
- func (h *Handlers) New(c *mizu.Ctx) error
- func (h *Handlers) Resolve(next mizu.Handler) mizu.Handler
- func (h *Handlers) Show(c *mizu.Ctx) error
- func (h *Handlers) ToggleCommentReaction(c *mizu.Ctx) error
- func (h *Handlers) ToggleIssueReaction(c *mizu.Ctx) error
- func (h *Handlers) ToggleState(c *mizu.Ctx) error
- type Qualifier
- type Query
- func (q *Query) ActiveLabels() []string
- func (q *Query) AddLabel(name string) string
- func (q *Query) Encode() string
- func (q *Query) Filter(viewer *view.Viewer) domain.IssueQuery
- func (q *Query) NoAssignee() string
- func (q *Query) NoMilestone() string
- func (q *Query) RemoveLabel(name string) string
- func (q *Query) SetAssignee(login string) string
- func (q *Query) SetAuthor(login string) string
- func (q *Query) SetMilestone(title string) string
- func (q *Query) SetSort(key string) string
- func (q *Query) WithState(s string) string
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Deps ¶
type Deps struct {
Issues *domain.IssueService
Repos *domain.RepoService
URLs *presenter.URLBuilder
Render *render.Set
View *view.Builder
Markup *markup.Renderer
Logger *slog.Logger
}
Deps are the issues handlers' dependencies: the domain issue service for every read and write, the repo service to resolve and read-gate the repository, the presenter for avatar URLs, the render set, the view builder for the shell chrome, the shared markup renderer for comment bodies, and a logger for the notices the list view emits. A nil markup renderer falls back to escaped comment source.
type Handlers ¶
type Handlers struct {
// contains filtered or unexported fields
}
Handlers is the issues handler set. One is built at boot and shared; it holds no per-request state.
func (*Handlers) Create ¶
Create opens an issue from the new-issue form and redirects to its detail page. A missing title re-renders the form with the values preserved and the message inline (the no-JS validation path), so a typo never loses the draft. The labels, assignees, and milestone the prefill carried apply through the same service input a REST create uses. The service authorizes write access; a viewer without it gets the themed 403. See implementation/08 section 10.
func (*Handlers) CreateComment ¶
CreateComment appends a comment and redirects to its permalink. An empty body re-renders the issue with the message inline rather than posting a blank comment.
func (*Handlers) DeleteComment ¶
DeleteComment removes a comment and redirects to the issue.
func (*Handlers) EditComment ¶
EditComment updates a comment body and redirects to its permalink.
func (*Handlers) EditSidebar ¶
EditSidebar applies the label and assignee edits from the sidebar form. The service authorizes write access and resolves unknown labels or assignees the same way it does on create, so the handler only parses the comma lists and redirects back to the issue.
func (*Handlers) EditTitle ¶
EditTitle renames the issue. An empty title re-renders the issue with the message inline.
func (*Handlers) Index ¶
Index renders the issues list with its search-and-filter bar. The ?q= string is the single source of filter truth: it is parsed once, projected to the domain query the API also uses, and re-composed server-side for every tab and chip so the page filters with no JavaScript. An is:pr query on this index is not a filter; it redirects to /pulls with the same query so the issues index stays issue-implicit. See implementation/08 sections 2 and 3.
func (*Handlers) Labels ¶ added in v0.1.3
Labels renders the repository's label list: GET /{owner}/{repo}/labels. Each row is the same color chip the issue rows wear, linking to the issues index filtered to that label, plus the label description. The list is read through the same ListLabels the REST endpoint serves, in name order. Label management stays in the REST API and the issue sidebar; this page is the browse surface. See spec 02 section 3.6.
func (*Handlers) Milestone ¶ added in v0.1.3
Milestone renders one milestone's page: GET /{owner}/{repo}/milestone/{number}, github.com's singular form. The header block carries the progress and due date; below it the milestone's issues render as the same bounded rows the issues index uses, tabbed open (?closed=1 for closed). A missing milestone is the soft 404.
func (*Handlers) Milestones ¶ added in v0.1.3
Milestones renders the milestone list: GET /{owner}/{repo}/milestones, with ?state=closed for the closed tab. One all-states read feeds both the rows and the honest tab counts. Milestone management stays in the REST API; this page is the browse surface. See spec 02 section 3.6.
func (*Handlers) New ¶
New renders the new-issue form, seeded from the documented prefill query (?title=&body=&labels=&assignees=&milestone=&template=). A viewer who cannot write still sees the form shell, and the create handler authorizes the submit, so the affordance and the action stay consistent. See implementation/08 section 10.
func (*Handlers) Resolve ¶
Resolve loads the repository named by the {owner} and {repo} path parameters, read-gated for the viewer, and stores it on the context. A missing repository, or a private one the viewer cannot see, renders the same 404, so a private repo never leaks through the status code. This mirrors the code-browsing Resolve so the two surfaces gate identically. It is the one place the repo is loaded; every handler reads it back with repoFromContext.
func (*Handlers) Show ¶
Show renders the issue detail page: the title and state header, the comment timeline (the opening body first, then the comments oldest-first), the sidebar with the labels, assignees, and milestone, and the composer. A missing issue, or one in a repo the viewer cannot see, renders the soft 404, never a 403. See implementation/08 section 4.
func (*Handlers) ToggleCommentReaction ¶
ToggleCommentReaction adds or removes the viewer's reaction of the submitted content on a comment, returning to the comment's anchor on the issue page.
func (*Handlers) ToggleIssueReaction ¶
ToggleIssueReaction adds or removes the viewer's reaction of the submitted content on the issue body.
func (*Handlers) ToggleState ¶
ToggleState closes or reopens the issue, optionally posting a comment in the same submit (the "Close with comment" path). The target state is derived from the issue's current state, so the one button does the right thing either way.
type Qualifier ¶
Qualifier is one key:value token, with the leading '-' negation preserved so the composer can round-trip a query it does not itself project.
type Query ¶
type Query struct {
Raw string // the literal q string, for the search input value
State string // "open" | "closed" | "" (unset, treated as open default)
Type string // "issue" (this index) | "pr" (the handler redirects to /pulls)
Terms []string // bare full-text words
Quals []Qualifier // every key:value, in source order, negation preserved
}
Query is the parsed and re-composable ?q= filter. It is the single source of truth for the issues index filter: the index handler parses it once, the templates call its composer methods to build dropdown and chip hrefs server-side, and Filter projects it to the domain query the API also uses.
func DefaultQuery ¶
func DefaultQuery() *Query
DefaultQuery is what an index visited with no ?q= behaves as, and what the "clear filters" link restores (implementation/08 section 1.6, 2.1).
func ParseQuery ¶
ParseQuery tokenizes the raw q string into a Query. It never errors: it cannot, because a browser can submit anything. A whitespace-only q yields the default.
func (*Query) ActiveLabels ¶
ActiveLabels returns the active label set sorted for a stable chip order.
func (*Query) AddLabel ¶
AddLabel appends a label: qualifier (labels are an AND set, so repeats stack). Re-adding a present label is a no-op so a chip click stays idempotent.
func (*Query) Encode ¶
Encode is the URL-encoded q value alone (no leading ?), for a caller that composes the query string itself.
func (*Query) Filter ¶
func (q *Query) Filter(viewer *view.Viewer) domain.IssueQuery
Filter projects the parsed Query into the domain.IssueQuery the REST list endpoint (implementation/02 section 4.7) also builds, so the UI and API agree field for field on what a query selects. @me resolves to the viewer login here, the only viewer-relative rewrite, so the domain query carries a concrete login. Qualifiers the domain does not model are left in the Query for round-tripping but do not narrow the result (see the as-built note at the top of this file).
func (*Query) NoAssignee ¶
NoAssignee sets no:assignee and drops any concrete assignee:.
func (*Query) NoMilestone ¶
NoMilestone sets no:milestone and drops any concrete milestone:.
func (*Query) RemoveLabel ¶
RemoveLabel drops a label: qualifier, the active-chip dismiss link.
func (*Query) SetAssignee ¶
SetAssignee replaces any assignee: qualifier with the given login and clears a no:assignee that would contradict it.
func (*Query) SetMilestone ¶
SetMilestone replaces any milestone: qualifier with the given title and clears a contradicting no:milestone.