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/reactions/v0

Realm
Open in gnoweb ↗

Overview

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

Files (3)

  • README.mdmarkdown
  • gnomod.tomltoml
  • reactions.gnogno
reactions.gnogno
1// Package reactions is an emoji reaction board.2//3// Each topic collects emoji reactions. A caller may register at most one4// reaction per topic; the emoji they picked increments a per-topic per-emoji5// tally. Render lists every topic with its emoji tallies and total count.6package reactions78import (9	"sort"10	"strconv"11	"strings"1213	"chain"14	"chain/runtime/unsafe"1516	"gno.land/p/nt/avl/v0"17)1819// topic holds the tallies and the set of callers who already reacted.20type topic struct {21	name    string22	tallies *avl.Tree // emoji (string) -> count (int)23	voters  *avl.Tree // caller address (string) -> struct{}24	total   int25}2627// topics maps topic name -> *topic, kept in an avl.Tree for deterministic order.28var topics = avl.NewTree()2930func getOrPanic(name string) *topic {31	v := topics.Get(name)32	if v == nil {33		panic("topic does not exist: " + name)34	}35	return v.(*topic)36}3738func newTopic(name string) *topic {39	t := &topic{40		name:    name,41		tallies: avl.NewTree(),42		voters:  avl.NewTree(),43	}44	topics.Set(name, t)45	return t46}4748// CreateTopic registers a new empty topic. It is optional: React auto-creates49// a topic if it does not exist yet. Panics if the topic already exists or the50// name is empty.51func CreateTopic(cur realm, name string) {52	name = strings.TrimSpace(name)53	if name == "" {54		panic("topic name must not be empty")55	}56	if topics.Has(name) {57		panic("topic already exists: " + name)58	}59	newTopic(name)60	chain.Emit("TopicCreated", "topic", name)61}6263// React adds the caller's reaction (an emoji) to a topic. Each caller may react64// at most once per topic. The topic is created on demand if missing. Panics on65// empty input or when the caller has already reacted to this topic.66func React(cur realm, name, emoji string) {67	name = strings.TrimSpace(name)68	emoji = strings.TrimSpace(emoji)69	if name == "" {70		panic("topic name must not be empty")71	}72	if emoji == "" {73		panic("emoji must not be empty")74	}7576	caller := unsafe.PreviousRealm().Address().String()7778	var t *topic79	if v := topics.Get(name); v != nil {80		t = v.(*topic)81	} else {82		t = newTopic(name)83	}8485	if t.voters.Has(caller) {86		panic("caller already reacted to this topic: " + name)87	}88	t.voters.Set(caller, struct{}{})8990	count := 091	if v := t.tallies.Get(emoji); v != nil {92		count = v.(int)93	}94	t.tallies.Set(emoji, count+1)95	t.total++9697	chain.Emit("Reacted", "topic", name, "emoji", emoji)98}99100// TopicCount returns the number of topics on the board.101func TopicCount() int {102	return topics.Size()103}104105// emojiTally is used to sort a topic's tallies deterministically.106type emojiTally struct {107	emoji string108	count int109}110111// byCountDesc sorts tallies by count descending, then emoji ascending.112type byCountDesc []emojiTally113114func (s byCountDesc) Len() int      { return len(s) }115func (s byCountDesc) Swap(i, j int) { s[i], s[j] = s[j], s[i] }116func (s byCountDesc) Less(i, j int) bool {117	if s[i].count != s[j].count {118		return s[i].count > s[j].count119	}120	return s[i].emoji < s[j].emoji121}122123func (t *topic) sortedTallies() []emojiTally {124	out := make([]emojiTally, 0, t.tallies.Size())125	t.tallies.Iterate("", "", func(key string, value interface{}) bool {126		out = append(out, emojiTally{emoji: key, count: value.(int)})127		return false128	})129	sort.Stable(byCountDesc(out))130	return out131}132133// tallyRow renders a topic's tallies as e.g. "👍 3  ❤️ 5  🎉 1".134func (t *topic) tallyRow() string {135	if t.tallies.Size() == 0 {136		return "_no reactions yet_"137	}138	rows := t.sortedTallies()139	parts := make([]string, 0, len(rows))140	for _, r := range rows {141		parts = append(parts, r.emoji+" "+strconv.Itoa(r.count))142	}143	return strings.Join(parts, "  ")144}145146// Render returns a Markdown view of the board. If path is a non-empty topic147// name, only that topic is shown; otherwise all topics are listed.148func Render(path string) string {149	path = strings.TrimSpace(strings.Trim(path, "/"))150151	if path != "" {152		v := topics.Get(path)153		if v == nil {154			return "# Reaction Board\n\nTopic not found: `" + path + "`\n"155		}156		t := v.(*topic)157		var sb strings.Builder158		sb.WriteString("# Reaction Board — " + t.name + "\n\n")159		sb.WriteString(t.tallyRow() + "\n\n")160		sb.WriteString("**Total reactions:** " + strconv.Itoa(t.total) + "\n")161		return sb.String()162	}163164	var sb strings.Builder165	sb.WriteString("# Reaction Board\n\n")166	if topics.Size() == 0 {167		sb.WriteString("_No topics yet. Call `React(cur, topic, emoji)` to start._\n")168		return sb.String()169	}170171	grand := 0172	topics.Iterate("", "", func(key string, value interface{}) bool {173		t := value.(*topic)174		grand += t.total175		sb.WriteString("## " + t.name + "\n\n")176		sb.WriteString(t.tallyRow() + "\n\n")177		sb.WriteString("Total: " + strconv.Itoa(t.total) + "\n\n")178		return false179	})180181	sb.WriteString("---\n\n")182	sb.WriteString("**Topics:** " + strconv.Itoa(topics.Size()) +183		" · **Reactions:** " + strconv.Itoa(grand) + "\n")184	return sb.String()185}186

Functions

  • CreateTopic(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)

  • 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}, name string, emoji string)

  • Render(path string) string

  • TopicCount() int

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.