Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

render.gno

3.58 Kb · 128 lines
  1package kitindexdemo
  2
  3import (
  4	"strconv"
  5
  6	"gno.land/p/moul/kit/store/v0"
  7	"gno.land/p/moul/kit/ui/v0"
  8	"gno.land/p/moul/md/v0"
  9	"gno.land/p/moul/realmpath/v0"
 10)
 11
 12// realmURL is this realm as gnoweb serves it. Links are absolute because the
 13// path ends in /v0: a relative "./kitindexdemo:tag/x" resolves against the
 14// parent directory to .../kitindexdemo/kitindexdemo:tag/x, which does not
 15// exist. Not derived at runtime: a Render has no cur, and CurrentRealm there
 16// is the caller. TestRealmURLMatchesTheModule pins it to the module line.
 17const realmURL = "/r/moul/x/kitindexdemo/v0"
 18
 19// Render is the whole public surface. Three pages: the tag list, one tag, one
 20// author.
 21//
 22// Every row goes through ui.NewTable rather than concatenated pipes, and every
 23// caller string through ui.Cell, because a public path is immutable and a
 24// Render that ships wrong ships forever.
 25func Render(path string) string {
 26	req := realmpath.Parse(path)
 27	switch req.PathPart(0) {
 28	case "":
 29		return renderIndex(req)
 30	case "tag":
 31		return renderTag(req.PathPart(1))
 32	case "author":
 33		return renderAuthor(req.PathPart(1))
 34	default:
 35		return md.H1("Not found") + ui.Empty("No such page.")
 36	}
 37}
 38
 39func renderIndex(req *realmpath.Request) string {
 40	// The page number is user input: clamp it into 1..pages, or ?page=-1
 41	// renders an empty board under a footer saying "page -1 of 2".
 42	pages := byTag.Pages(PageSize)
 43	page := 1
 44	if n, err := strconv.Atoi(req.Query.Get("page")); err == nil {
 45		page = n
 46	}
 47	if page < 1 {
 48		page = 1
 49	}
 50	if page > pages {
 51		page = pages
 52	}
 53
 54	t := ui.NewTable("tag", "notes")
 55	for _, e := range byTag.Page(page, PageSize) {
 56		t.Row(
 57			md.Link(e.Key, realmURL+":tag/"+e.Key),
 58			strconv.Itoa(len(e.IDs)),
 59		)
 60	}
 61
 62	return ui.Join("\n",
 63		md.H1("Notes by tag"),
 64		md.Paragraph(strconv.Itoa(notes.Len())+" note(s) under "+
 65			strconv.Itoa(byTag.Keys())+" tag(s), by "+
 66			strconv.Itoa(byAuthor.Keys())+" author(s)."),
 67		t.OrEmpty("Nothing posted yet."),
 68		pager(page, pages),
 69		md.Paragraph(ui.Action("Post a note", "Post", "tag", "", "body", "")),
 70	)
 71}
 72
 73func renderTag(tag string) string {
 74	if tag == "" {
 75		return md.H1("Not found") + ui.Empty("No tag given.")
 76	}
 77	return md.H1("#"+ui.Cell(tag)) + notesTable(byTag.Lookup(tag), "No notes under this tag.")
 78}
 79
 80func renderAuthor(addr string) string {
 81	if addr == "" {
 82		return md.H1("Not found") + ui.Empty("No author given.")
 83	}
 84	return md.H1("Notes by "+ui.AddrOf(addr)) +
 85		notesTable(byAuthor.Lookup(addr), "This address has posted nothing.")
 86}
 87
 88// notesTable renders a set of ids as rows, skipping any id the store no longer
 89// holds.
 90//
 91// Skipping rather than panicking: an index and a store are two containers, and
 92// a reader should see the records that exist rather than a page that aborts.
 93// Retract keeps the two in step, so a miss here means a bug worth surviving,
 94// not worth hiding.
 95func notesTable(hits []store.ID, empty string) string {
 96	t := ui.NewTable("#", "author", "tag", "note", "height")
 97	for _, id := range hits {
 98		v, ok := notes.Get(id)
 99		if !ok {
100			continue
101		}
102		n := v.(*note)
103		t.Row(
104			id.String(),
105			ui.Addr(n.Author),
106			md.InlineCode(n.Tag),
107			ui.Cell(n.Body),
108			strconv.FormatInt(n.Height, 10),
109		)
110	}
111	return t.OrEmpty(empty)
112}
113
114// pager renders the previous/next links, or nothing when there is one page.
115func pager(page, pages int) string {
116	if pages < 2 {
117		return ""
118	}
119	out := ""
120	if page > 1 {
121		out += md.Link("previous", realmURL+":?page="+strconv.Itoa(page-1)) + " "
122	}
123	out += "page " + strconv.Itoa(page) + " of " + strconv.Itoa(pages)
124	if page < pages {
125		out += " " + md.Link("next", realmURL+":?page="+strconv.Itoa(page+1))
126	}
127	return out
128}