PathrockNetwork Gno Explorer
HomeBlocksTransactionsRealmsPackagesValidatorsAnalytics

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/x/daily/blog/v0

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
v0
Namespace
moul / x / daily / blog
Files
3 (README)(gnomod.toml)
Exported functions
3
Module
gno.land/r/moul/x/daily/blog/v0
gno
0.9

Files (3)

  • README.mdmarkdown
  • gnomod.tomltoml
  • blog.gnogno
blog.gnogno
1// Package blog is a multi-author on-chain blog realm for gno.land.2//3// Any caller can Publish a post; posts are stored persistently and rendered4// newest-first. Each post records its author (the calling address) and the5// block height at which it was published.6package blog78import (9	"strconv"10	"strings"1112	"chain"13	"chain/runtime"14	"chain/runtime/unsafe"1516	"gno.land/p/nt/avl/v0"17)1819// Post is a single blog entry.20type Post struct {21	ID     int22	Title  string23	Body   string24	Author address25	Height int6426}2728// posts holds every published post keyed by its zero-padded id (for29// deterministic ordering) and nextID is the id assigned to the next post.30var (31	posts  avl.Tree // key: zero-padded id string -> *Post32	nextID int33)3435// key returns a fixed-width, zero-padded key so avl.Tree iteration is ordered36// by numeric id (and thus by publication order).37func key(id int) string {38	s := strconv.Itoa(id)39	for len(s) < 12 {40		s = "0" + s41	}42	return s43}4445// Publish creates a new post authored by the caller and returns its id.46// It is a state-mutating exported function, so it takes `cur realm`.47func Publish(cur realm, title string, body string) int {48	title = strings.TrimSpace(title)49	body = strings.TrimSpace(body)50	if title == "" {51		panic("blog: title must not be empty")52	}53	if body == "" {54		panic("blog: body must not be empty")55	}5657	id := nextID58	nextID++5960	p := &Post{61		ID:     id,62		Title:  title,63		Body:   body,64		Author: unsafe.PreviousRealm().Address(),65		Height: runtime.ChainHeight(),66	}67	posts.Set(key(id), p)6869	chain.Emit(70		"PostPublished",71		"id", strconv.Itoa(id),72		"author", p.Author.String(),73	)74	return id75}7677// PostCount returns the number of published posts.78func PostCount() int {79	return posts.Size()80}8182// snippet returns a short one-line preview of the body.83func snippet(body string) string {84	// collapse newlines so the preview stays on a single markdown line85	s := strings.ReplaceAll(body, "\n", " ")86	s = strings.TrimSpace(s)87	const max = 14088	if len(s) > max {89		return s[:max] + "…"90	}91	return s92}9394// Render lists all posts newest-first at the root, or a single post's full95// text at path "/<id>". Render is NOT a crossing function.96func Render(path string) string {97	path = strings.TrimSpace(path)98	if path == "" || path == "/" {99		return renderList()100	}101	idStr := strings.TrimPrefix(path, "/")102	id, err := strconv.Atoi(idStr)103	if err != nil {104		return "# Not found\n\nInvalid post path: `" + path + "`\n\n[← back](/r/REPLACE_ADDR/blog)\n"105	}106	v := posts.Get(key(id))107	if v == nil {108		return "# Not found\n\nNo post with id " + idStr + ".\n\n[← back](/r/REPLACE_ADDR/blog)\n"109	}110	return renderPost(v.(*Post))111}112113func renderList() string {114	var b strings.Builder115	b.WriteString("# 📝 Blog\n\n")116	if posts.Size() == 0 {117		b.WriteString("_No posts yet. Be the first to `Publish`._\n")118		return b.String()119	}120	b.WriteString(strconv.Itoa(posts.Size()) + " post(s), newest first.\n\n")121122	// avl iterates ascending; iterate in reverse for newest-first.123	posts.ReverseIterate("", "", func(_ string, v interface{}) bool {124		p := v.(*Post)125		b.WriteString("## [")126		b.WriteString(p.Title)127		b.WriteString("](/r/REPLACE_ADDR/blog:/")128		b.WriteString(strconv.Itoa(p.ID))129		b.WriteString(")\n\n")130		b.WriteString("by `")131		b.WriteString(p.Author.String())132		b.WriteString("` · height ")133		b.WriteString(strconv.FormatInt(p.Height, 10))134		b.WriteString("\n\n")135		b.WriteString(snippet(p.Body))136		b.WriteString("\n\n---\n\n")137		return false138	})139	return b.String()140}141142func renderPost(p *Post) string {143	var b strings.Builder144	b.WriteString("# ")145	b.WriteString(p.Title)146	b.WriteString("\n\n")147	b.WriteString("by `")148	b.WriteString(p.Author.String())149	b.WriteString("` · height ")150	b.WriteString(strconv.FormatInt(p.Height, 10))151	b.WriteString("\n\n")152	b.WriteString(p.Body)153	b.WriteString("\n\n---\n\n[← back to all posts](/r/REPLACE_ADDR/blog)\n")154	return b.String()155}156

Functions

  • PostCount() int

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

  • Render(path string) 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.