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/g12yg9nh4ncma44emgm8msxe8aavzywt0p95tanv/home

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
home
Namespace
g12yg9nh4ncma44emgm8msxe8aavzywt0p95tanv
Files
5 (gnomod.toml)
Exported functions
15
Module
gno.land/r/g12yg9nh4ncma44emgm8msxe8aavzywt0p95tanv/home
gno
0.9

Files (5)

  • gnomod.tomltoml
  • content.gnogno
  • home.gnogno
  • widgets.gnogno
  • writes.gnogno
home.gnogno
1// Package home is a personal home realm. gno.land also shows it as the2// profile page at /u/<name>, which always calls Render("") and reads its3// links against /u/<name>: every link here is absolute.4package home56import (7	"chain/runtime"8	"strconv"9	"strings"1011	"gno.land/p/nt/avl/v0"12)1314const (15	homeTab      = "home"16	workTab      = "work"17	blogTab      = "blog"18	guestbookTab = "guestbook"1920	maxTabs       = 821	maxTabName    = 2022	maxPageBytes  = 16 * 1024 // so all pages together stay under 128 KB23	maxLineRunes  = 14024	maxBadges     = 625	maxSlugBytes  = 6026	maxTitleRunes = 10027	pageSize      = 2028	latestPosts   = 329	seqWidth      = 103031	errUnauthorized = "home: unauthorized"32	errMasterKey    = "home: needs the master key, not a session key"33	errOwnerOnly    = "home: owner only"34	errPendingOnly  = "home: pending owner only"35	errInvalidInput = "home: invalid input: "36	errSigned       = "home: already signed"37	errNoEntry      = "home: no such entry"38	errNoTab        = "home: no such tab"39	errNoPost       = "home: no such post"40	errTabsRequired = "home: home, blog and guestbook tabs are required"41	errUgnotOnly    = "home: send ugnot only"42)4344type profileData struct {45	name    string46	tagline string47}4849type badge struct {50	label string51	value string52	url   string53	row   bool // starts a new row54}5556type nowLine struct {57	text   string58	height int6459}6061type entry struct {62	msg    string63	height int6464	hidden bool65}6667// post dates are UTC days of the block time, "2006-01-02".68type post struct {69	title     string70	body      string71	published string72	edited    string // "" until the first edit73	seq       string // key in postOrder74}7576type tipStats struct {77	count int78	total int6479}8081var (82	owner        address83	pendingOwner address // set by TransferOwnership, cleared by AcceptOwnership84	base         string  // "/r/<name>/home"; every generated link starts with it85	profile      profileData86	badges       []badge87	now          nowLine88	tabs         []string          // tab order: home first, guestbook last89	pages        map[string]string // markdown per tab90	waves        int               // wave count; anyone can wave again91	entries      *avl.Tree         // address -> *entry92	order        *avl.Tree         // zero-padded sequence -> address93	posts        *avl.Tree         // slug -> *post94	postOrder    *avl.Tree         // zero-padded sequence -> slug95	postSeq      int               // last post sequence; never reused96	tips         tipStats97	updated      int64 // block height of the last owner write98)99100// init records the deployer as owner. Under mainnet's approval flow it runs101// as the submitter, not the approver. No AssertOriginCall: init isn't a call.102func init(cur realm) {103	owner = callerOf(cur)104	base = strings.TrimPrefix(cur.PkgPath(), runtime.ChainDomain())105	setDefaults()106}107108// setDefaults writes the initial state (content.gno holds the personal part).109func setDefaults() {110	profile = defaultProfile()111	badges = defaultBadges()112	now = nowLine{}113	pendingOwner = ""114	tabs = []string{homeTab, workTab, blogTab, guestbookTab}115	pages = map[string]string{116		homeTab:      defaultHome(),117		workTab:      defaultWork,118		blogTab:      defaultBlog,119		guestbookTab: defaultGuestbook,120	}121	waves = 0122	entries = avl.NewTree()123	order = avl.NewTree()124	posts = avl.NewTree()125	postOrder = avl.NewTree()126	postSeq = 0127	tips = tipStats{}128	updated = runtime.ChainHeight()129}130131// callerOf is the account guard: a live cur and a direct call from an132// account. It must not call AssertOriginCall, which refuses every call from133// inside a named helper.134func callerOf(cur realm) address {135	if !cur.IsCurrent() || !cur.Previous().IsUserCall() {136		panic(errUnauthorized)137	}138	return cur.Previous().Address()139}140141// assertOwner is the owner guard.142func assertOwner(cur realm) {143	if callerOf(cur) != owner {144		panic(errOwnerOnly)145	}146}147148// Owner returns the current owner.149func Owner() address {150	return owner151}152153// Render shows one tab, or one post at blog/<slug>. An unknown path shows154// Home and an unknown post shows Blog. The path is only compared against155// tab names or looked up as a slug, and never written into the page.156func Render(path string) string {157	tab, query, _ := strings.Cut(path, "?")158	if slug, ok := strings.CutPrefix(tab, blogTab+"/"); ok {159		if p, ok := posts.Get(slug).(*post); ok {160			return frame("", postMD(p)) // not a tab: no tab is bold161		}162		tab = blogTab163	}164	if _, ok := pages[tab]; !ok {165		tab = homeTab166	}167	return frame(tab, expand(pages[tab], tab, parsePage(query)))168}169170// frame puts the tab bar above a page and the footer below it.171func frame(tab, md string) string {172	return tabBar(tab) + "\n\n" + md + "\n\n" + footer()173}174175func tabURL(t string) string {176	if t == homeTab {177		return base178	}179	return base + ":" + t180}181182// tabLabel capitalizes a tab name; names are ASCII by validName.183func tabLabel(t string) string {184	return strings.ToUpper(t[:1]) + t[1:]185}186187// tabBar draws every tab as a linked pill, the current one in brand green.188// The current tab is linked too, because gnoweb shows an unlinked image as189// a block and the row would break. Blog stays out until there is a post.190func tabBar(current string) string {191	parts := make([]string, 0, len(tabs))192	for _, t := range tabs {193		s := segSoft(tabLabel(t))194		switch {195		case t == current:196			s = segOn(tabLabel(t))197		case t == blogTab && posts.Size() == 0:198			continue // no posts yet199		}200		parts = append(parts, pillLink(tabLabel(t), tabURL(t), tabH, 0, s))201	}202	return strings.Join(parts, " ")203}204205func footer() string {206	return "---\n\nOwner " + userLink(owner) +207		" · updated at block " + strconv.FormatInt(updated, 10) +208		" · [Source](" + base + "$source)\n"209}210211// parsePage reads page=N from the query; anything invalid is page 1.212func parsePage(query string) int {213	for _, kv := range strings.Split(query, "&") {214		k, v, _ := strings.Cut(kv, "=")215		if k != "page" {216			continue217		}218		n, err := strconv.Atoi(v)219		if err != nil || n < 1 {220			return 1221		}222		return n223	}224	return 1225}226

Functions

  • AcceptOwnership(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})

  • Hide(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}, author string)

  • Owner() string

  • RemovePage(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}, tab string)

  • RemovePost(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}, slug string)

  • Render(path string) string

  • SetBadges(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}, spec string)

  • SetNow(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}, text string)

  • SetPage(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}, tab string, md string)

  • SetPost(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}, slug string, title string, body string)

  • SetProfile(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}, name string, tagline string)

  • Sign(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}, msg string)

  • Tip(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})

  • TransferOwnership(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}, newOwner string)

  • Wave(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})

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.