package web
import (
"context"
"errors"
"net/http"
"github.com/go-chi/chi/v5"
"sourcecraft.dev/bigbes/sr-ht-ecore/chrome"
"sourcecraft.dev/bigbes/sr-ht-dolt/browse"
"sourcecraft.dev/bigbes/sr-ht-dolt/core"
)
// handleView renders a specialized alternative view of a repository. The view
// is selected by the {view} URL slug and must both exist in a.views and Apply
// to the tables at the resolved ref; otherwise the request is a 404 (the view
// simply does not exist for this repo). The generic table browser remains
// reachable via the tree/table routes regardless.
//
// The ref is taken from ?ref= (default: the repo's default branch). The view's
// own Template() is rendered with a fixed envelope: the chrome, the repo, the
// ref and branch list, the applicable view tabs, and the opaque value the view
// produced from Build (as .Data).
func (a *app) handleView(w http.ResponseWriter, r *http.Request) {
repo, _, _, ok := a.loadRepoForBrowse(w, r)
if !ok {
return
}
sess, ok := a.openBrowse(w, r, repo)
if !ok {
return
}
defer sess.Close()
ref := r.URL.Query().Get("ref")
if ref == "" {
branches, err := sess.Branches(r.Context())
if err != nil {
http.Error(w, "failed to list branches", http.StatusInternalServerError)
return
}
ref = browse.DefaultBranch(branches)
}
tables, err := sess.Tables(r.Context(), ref)
if err != nil {
if errors.Is(err, browse.ErrRefNotFound) {
a.notFound(w, r)
return
}
http.Error(w, "failed to read tables", http.StatusInternalServerError)
return
}
// Find the requested view; an unknown slug or a view that does not fingerprint
// these tables is reported as not found — the view does not exist here.
slug := chi.URLParam(r, "view")
var view View
for _, v := range a.views {
if v.Name() == slug {
view = v
break
}
}
if view == nil || !view.Applies(tables) {
a.notFound(w, r)
return
}
data, err := view.Build(r.Context(), sess, repo, ref, r.URL.Query())
if err != nil {
http.Error(w, "failed to build view", http.StatusInternalServerError)
return
}
// Re-list branches for the chrome/tab bar. Cheap and keeps the ref selector
// consistent whether or not ?ref= was supplied.
branches, _ := sess.Branches(r.Context())
envelope := struct {
chrome.Page
Repo *core.Repo
Ref string
Branches []browse.Branch
Views []View
Head *browse.CommitInfo
Data any
}{
Page: a.page(r, view.Label()+" — "+repo.OwnerName+"/"+repo.Name),
Repo: repo,
Ref: ref,
Branches: branches,
Views: applicableViews(a.views, tables),
Head: headCommit(r.Context(), sess, ref),
Data: data,
}
a.render(w, http.StatusOK, pageName(view.Template()), envelope)
}
// headCommit reads the head commit of ref for the envelope's freshness line: a
// board rendered from a store that stopped receiving pushes yesterday is
// otherwise indistinguishable from a current one.
//
// It returns nil rather than an error, and that is the whole contract. A
// database with no commits, or a Log that fails, must still render the view —
// this is decoration on top of an answer, and it may never be the reason a
// reader gets a 500 instead of a board. The partial renders nothing for nil.
func headCommit(ctx context.Context, sess BrowseSession, ref string) *browse.CommitInfo {
commits, _, err := sess.Log(ctx, ref, "", 1)
if err != nil || len(commits) == 0 {
return nil
}
return &commits[0]
}