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

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

todos.gno

2.99 Kb · 114 lines
  1// Package todos is a shared, on-chain to-do board realm.
  2//
  3// Anyone can add an item; the caller's address is recorded as the author.
  4// Items can be toggled (open/done) or removed. Render exposes the board as
  5// Markdown checkboxes with open/done counts, newest item first.
  6package todos
  7
  8import (
  9	"strconv"
 10	"strings"
 11
 12	"gno.land/p/nym-moultest123/kit/ui/v0"
 13	"gno.land/p/nym-moultest123/kit/store/v0"
 14)
 15
 16// item is one entry on the board. `address` is the uverse bech32 type — it is
 17// persistable (unlike a `realm` value), so it is safe to store across txs.
 18// It carries no id field: the id belongs to the store, which hands it back on
 19// lookup and iteration.
 20type item struct {
 21	text   string
 22	done   bool
 23	author address
 24}
 25
 26// items assigns the item ids. v1 kept its own nextID plus an idKey() that
 27// zero-padded to width 12, which stopped ordering Render past 10^12.
 28var items = store.Named("todos: item")
 29
 30// Add appends a new item authored by the caller and returns its id.
 31//
 32// Crossing function: takes `cur realm` first (gno 0.9 interrealm convention),
 33// authenticates with cur.IsCurrent() before trusting cur.Previous().
 34func Add(cur realm, text string) int {
 35	if !cur.IsCurrent() {
 36		panic("spoofed realm")
 37	}
 38	text = strings.TrimSpace(text)
 39	if text == "" {
 40		panic("todos: text must not be empty")
 41	}
 42
 43	author := cur.Previous().Address()
 44
 45	return int(items.Add(&item{
 46		text:   text,
 47		done:   false,
 48		author: author,
 49	}))
 50}
 51
 52// Toggle flips the done state of the item with the given id.
 53func Toggle(cur realm, id int) {
 54	if !cur.IsCurrent() {
 55		panic("spoofed realm")
 56	}
 57	it := items.MustGet(store.ID(id)).(*item)
 58	it.done = !it.done
 59}
 60
 61// Remove deletes the item with the given id.
 62func Remove(cur realm, id int) {
 63	if !cur.IsCurrent() {
 64		panic("spoofed realm")
 65	}
 66	if _, removed := items.Remove(store.ID(id)); !removed {
 67		panic("todos: item #" + strconv.Itoa(id) + " not found")
 68	}
 69}
 70
 71// counts returns the number of open and done items.
 72func counts() (open, done int) {
 73	items.Each(func(_ store.ID, v any) {
 74		if v.(*item).done {
 75			done++
 76		} else {
 77			open++
 78		}
 79	})
 80	return open, done
 81}
 82
 83// Render returns the board as Markdown. Newest item (highest id) first.
 84//
 85// Render is NOT a crossing function (no `cur realm` param) — it is read-only
 86// and invoked by gnoweb, not via MsgCall.
 87func Render(path string) string {
 88	open, done := counts()
 89
 90	var b strings.Builder
 91	b.WriteString("# Shared To-Do Board\n\n")
 92	b.WriteString(strconv.Itoa(open) + " open · ")
 93	b.WriteString(strconv.Itoa(done) + " done · ")
 94	b.WriteString(strconv.Itoa(open+done) + " total\n\n")
 95
 96	if open+done == 0 {
 97		b.WriteString("_No items yet. Add one with `Add(text)`._\n")
 98		return b.String()
 99	}
100
101	// The store iterates by id, so newest-first is one call rather than a
102	// collect-then-walk-backwards.
103	items.EachReverse(func(id store.ID, v any) {
104		it := v.(*item)
105		mark := " "
106		if it.done {
107			mark = "x"
108		}
109		b.WriteString("- [" + mark + "] #" + id.String() + " " +
110			it.text + " (" + ui.Addr(it.author) + ")\n")
111	})
112
113	return b.String()
114}