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.

blocks.gno

3.24 Kb · 85 lines
 1package config
 2
 3import "strings"
 4
 5// The notice blocks: two strings a realm drops at the top and the bottom of its
 6// Render, empty by default, so moul can put a warning, a changelog line or a
 7// bit of news on every realm at once or on one of them.
 8//
 9//	func Render(path string) string {
10//		return config.TopBlock() + body + config.BottomBlock()
11//	}
12//
13// Both are set with the ordinary Set, which is what keeps this realm's surface
14// from growing a function per idea:
15//
16//	Set block.top            "> Chain migration on Tuesday."   # every realm
17//	Set block.top@r/moul/gns "> v2 shipped, see the changelog" # this one only
18const (
19	// KeyBlockTop is the notice rendered above a realm's content.
20	KeyBlockTop = "block.top"
21	// KeyBlockBottom is the notice rendered below it.
22	KeyBlockBottom = "block.bottom"
23)
24
25// TopBlock returns the notice for the realm calling in, ready to concatenate
26// in front of its content. Empty when there is nothing to say, which is the
27// default and the usual case.
28//
29// Three things can appear, in this order, separated by blank lines:
30//
31//  1. the pause banner, when this realm or every realm is paused
32//  2. the global block.top
33//  3. this realm's own block.top
34//
35// The pause banner comes first because it is the one a reader has to see, and
36// it is included here rather than left to the realm so that guarding writes
37// with AssertWritable is enough to also explain the refusal on the page.
38//
39// Global and scoped are BOTH shown rather than one overriding the other: a
40// chain-wide warning and a per-realm changelog are different messages, and
41// dropping either because the other exists is the surprising behaviour.
42func TopBlock() string { return TopBlockFor(caller()) }
43
44// TopBlockFor is TopBlock for a named realm. Use it from a crossing function,
45// where the caller cannot be read off the stack, or to render another realm's
46// notice.
47func TopBlockFor(pkgPath string) string {
48	global, scoped := scopedPair(KeyBlockTop, pkgPath)
49	return joinBlocks(PauseFor(pkgPath).Notice(), global, scoped)
50}
51
52// BottomBlock returns the notice for the realm calling in, ready to
53// concatenate after its content.
54//
55// No pause banner here: one is enough, and the top is where it is read.
56func BottomBlock() string { return BottomBlockFor(caller()) }
57
58// BottomBlockFor is BottomBlock for a named realm.
59func BottomBlockFor(pkgPath string) string {
60	global, scoped := scopedPair(KeyBlockBottom, pkgPath)
61	return joinBlocks(global, scoped)
62}
63
64// joinBlocks assembles the non-empty parts into one markdown fragment.
65//
66// Each part is separated by a blank line, because two markdown blocks with
67// only a newline between them are one paragraph, and a blockquote followed
68// directly by text swallows the text into the quote.
69//
70// The result ends with a blank line when there is anything at all, so a realm
71// can write config.TopBlock() + body without a separator of its own and get
72// the same output whether or not a notice is set. Nothing to say means the
73// empty string, and the page is byte-for-byte what it was before.
74func joinBlocks(parts ...string) string {
75	var kept []string
76	for _, p := range parts {
77		if p = strings.TrimSpace(p); p != "" {
78			kept = append(kept, p)
79		}
80	}
81	if len(kept) == 0 {
82		return ""
83	}
84	return strings.Join(kept, "\n\n") + "\n\n"
85}