Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

scan.gno

3.02 Kb · 77 lines
 1package home
 2
 3import (
 4	"strings"
 5
 6	"chain/runtime"
 7
 8	"gno.land/p/moul/dynreplacer/v0"
 9	"gno.land/p/moul/mygnoscan/v0"
10	"gno.land/r/moul/config/v1"
11)
12
13// This file is the explorer half of the page: a handful of computed
14// placeholders that turn what this realm already knows (its own path, its
15// owner, the block it is rendering at) into links a reader can click.
16//
17// They are COMPUTED, not slots, so they are always current: the layout slot
18// says where a link goes on the page, and nothing in storage has to be
19// rewritten when the answer changes.
20//
21// The explorer they point at comes from gno.land/r/moul/config, not from a
22// constant here. That is the whole point of the indirection: moving every one
23// of moul's realms to a different explorer is one transaction against config,
24// not a redeploy of each realm that links to one. When config has no setting,
25// mygnoscan.DefaultBase answers, so this realm renders correctly on a chain
26// where nothing was ever configured.
27
28// scanPlaceholders are the reserved slugs this file answers for. They are
29// listed in reservedSlugs (home.gno) so a slot can never take one of these
30// names and shadow the computed value.
31var scanPlaceholders = []string{
32	"scan",       // the explorer base URL, as plain text
33	"scan.realm", // this realm on the explorer
34	"scan.me",    // the owner's account page
35	"scan.block", // the block this render is happening at
36	"scan.links", // the three above, on one line
37}
38
39// scanner returns the configured link builder.
40//
41// config.Scanner() is a read, and a cheap one: an avl lookup plus a struct.
42// It is called once per render and the result reused, rather than once per
43// placeholder, because dynreplacer may invoke several of the callbacks below
44// and each would otherwise repeat the cross-realm call.
45func scanner() mygnoscan.Scanner { return config.Scanner() }
46
47// registerScanCallbacks adds the explorer placeholders to r.
48//
49// dynreplacer only invokes a callback whose placeholder actually occurs in the
50// layout, so a layout using none of these costs one config.Scanner() call and
51// nothing else.
52func registerScanCallbacks(r *dynreplacer.Replacer) {
53	s := scanner()
54
55	r.RegisterCallback(":scan:", func() string { return s.Base() })
56	r.RegisterCallback(":scan.realm:", func() string {
57		return mygnoscan.Link("mygnoscan", s.Realm(realmPath))
58	})
59	r.RegisterCallback(":scan.me:", func() string {
60		return mygnoscan.Link("my account", s.Address(admin))
61	})
62	r.RegisterCallback(":scan.block:", func() string {
63		return mygnoscan.Link("this block", s.Block(runtime.ChainHeight()))
64	})
65	r.RegisterCallback(":scan.links:", func() string { return scanLinks(s) })
66}
67
68// scanLinks is the :scan.links: placeholder: everything above on one line, for
69// a layout that wants the footer and does not want to lay it out by hand.
70func scanLinks(s mygnoscan.Scanner) string {
71	parts := []string{
72		mygnoscan.Link("this realm", s.Realm(realmPath)),
73		mygnoscan.Link("my account", s.Address(admin)),
74		mygnoscan.Link("this block", s.Block(runtime.ChainHeight())),
75	}
76	return strings.Join(parts, " · ")
77}