PathrockNetwork Gno Explorer
HomeBlocksTransactionsTokensRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/r/moul/reactions/v0

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
v0
Namespace
moul / reactions
Files
5 (README)(gnomod.toml)
Exported functions
10
Module
gno.land/r/moul/reactions/v0
gno
0.9

Files (5)

  • README.mdmarkdown
  • gnomod.tomltoml
  • reactions.gnogno
  • render.gnogno
  • reactions_test.gnogno
render.gnogno
1package reactions23import (4	"strconv"5	"strings"67	"gno.land/p/moul/kit/ui/v0"8	rx "gno.land/p/moul/reactions/v0"9)1011// maxListed caps the board index. The number of pages is unbounded (anyone can12// react to any key), and a Render that grows with it would eventually be too13// expensive to serve. The cap is on the INDEX only: every page keeps its own14// view at /r/moul/reactions/v0:<page>, whether or not it is listed here.15const maxListed = 251617// Render serves two views:18//19//	""       the getting-started page: how to embed the block, plus the board20//	"<page>" one page's tally, where <page> is a package path21//22// The page view's path is the key itself, so the link under any embedded block23// is just this realm's path, a colon, and the page. That is why a page key may24// contain a colon of its own: "gno.land/r/you/blog:hello" reads back whole.25func Render(path string) string {26	if path == "" {27		return renderHome()28	}29	if !rx.ValidPage(path) {30		return "# Not a page\n\n" + ui.Inline(path) + " is not a page key: a key is a " +31			"package path, lowercase ASCII, at most " + strconv.Itoa(rx.MaxPageLen) +32			" bytes.\n\n[Back to the board](" + selfURL() + ")\n"33	}34	return renderPage(path)35}3637// selfURL is this realm's own gnoweb path, derived from realmPath the same way38// every link in the block is, so there is one rule and not two. A function and39// not a package-level var: a package-level var in a realm is realm STATE,40// persisted and re-read on every call, which is a strange thing for a string41// the compiler could have folded.42func selfURL() string { return rx.RealmURL(realmPath) }4344func renderHome() string {45	var b strings.Builder4647	b.WriteString("# Reactions\n\n")48	b.WriteString("A reaction block any realm can embed. One line in your `Render`, no state ")49	b.WriteString("on your side, and readers get one-click reactions keyed on the page they ")50	b.WriteString("are looking at.\n\n")51	b.WriteString("It is not a comment system, on purpose. There is no text field anywhere, ")52	b.WriteString("which is what lets it run with no moderation: a closed palette, one ")53	b.WriteString("reaction per address per page, changeable and removable.\n\n")5455	b.WriteString("## It looks like this\n\n")56	b.WriteString(blockFor(realmPath))57	b.WriteString("\nThat block is live and it is this realm's own page. Click one.\n\n")5859	b.WriteString("## Add it to your realm\n\n")60	b.WriteString("```go\n")61	b.WriteString("import \"" + realmPath + "\"\n\n")62	b.WriteString("func Render(path string) string {\n")63	b.WriteString("\treturn body + reactions.RenderBlock()\n")64	b.WriteString("}\n")65	b.WriteString("```\n\n")66	b.WriteString("That is the whole integration. `RenderBlock` takes no argument because it ")67	b.WriteString("does not need one: a plain read exported by one realm and called by ")68	b.WriteString("another opens no realm frame, so it sees YOUR path and uses it as the ")69	b.WriteString("page key. Nothing to register, nothing to configure.\n\n")70	b.WriteString("For one block per article rather than one per realm, name the page ")71	b.WriteString("yourself with the path you already serve:\n\n")72	b.WriteString("```go\n")73	b.WriteString("return body + reactions.RenderBlockFor(\"gno.land/r/you/blog:\" + path)\n")74	b.WriteString("```\n\n")75	b.WriteString("Reactions are recorded here, not in your realm, so redeploying yours ")76	b.WriteString("never loses them.\n\n")7778	b.WriteString("## The palette\n\n")79	t := ui.NewTable("key", "")80	for _, k := range rx.Keys() {81		t.Row("`"+k+"`", rx.Emoji(k))82	}83	b.WriteString(t.String())84	b.WriteString("\nSix, and the set is closed. `React` aborts on anything else, which is ")85	b.WriteString("the entire moderation policy.\n\n")8687	b.WriteString("## The board\n\n")88	b.WriteString(renderBoard())8990	return b.String()91}9293// renderBoard lists pages in key order, not by activity. Key order is the order94// the tree already walks, so the listing is stable between identical calls;95// ranking would need a second index maintained on every write, which is a lot96// of machinery for a table nobody navigates by.97func renderBoard() string {98	n := board.Pages()99	if n == 0 {100		return ui.Empty("No page has been reacted to yet.")101	}102103	t := ui.NewTable("page", "reactions", "last")104	board.Each(func(page string, p *rx.Page) bool {105		key, who, _ := p.Last()106		last := "-"107		if key != "" {108			last = rx.Emoji(key) + " " + ui.Addr(who)109		}110		t.Row(111			"["+ui.Cell(page)+"]("+rx.PageURL(realmPath, page)+")",112			strconv.Itoa(p.Total()),113			last,114		)115		return t.Len() >= maxListed116	})117118	out := t.String()119	if n > maxListed {120		out += "\n" + strconv.Itoa(n) + " pages in total, " + strconv.Itoa(maxListed) +121			" shown. Every page has its own view at `" + selfURL() + ":<page>`.\n"122	}123	return out124}125126func renderPage(page string) string {127	p := board.Page(page)128129	var b strings.Builder130	b.WriteString("# " + ui.Inline(page) + "\n\n")131	b.WriteString(blockFor(page))132	b.WriteString("\n")133134	if p.Total() == 0 {135		b.WriteString(ui.Empty("Nobody is reacting to this page right now."))136	} else {137		t := ui.NewTable("", "reaction", "addresses")138		for _, k := range rx.Keys() {139			if c := p.Count(k); c > 0 {140				t.Row(rx.Emoji(k), "`"+k+"`", strconv.Itoa(c))141			}142		}143		b.WriteString(t.String())144	}145146	if key, who, height := p.Last(); key != "" {147		b.WriteString("\nLast reaction: " + rx.Emoji(key) + " by " + ui.Addr(who) +148			" at height " + strconv.FormatInt(height, 10) + ".\n")149	}150151	b.WriteString("\n" + ui.ActionIn(realmPath, "Remove my reaction", "Unreact", "page", page))152	b.WriteString(" · [The board](" + selfURL() + ")\n")153	return b.String()154}155

Functions

  • Count(page string) int

  • CountOf(page string, key string) int

  • Last(page string) (key string, who string, height int64)

  • Pages() int

  • React(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, page string, key string)

  • ReactionOf(page string, who string) string

  • Render(path string) string

  • RenderBlock() string

  • RenderBlockFor(page string) string

  • Unreact(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, page string)

Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.

Rendered

RenderedRawgnoweb ↗

vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.