render.gno
4.26 Kb · 165 lines
1package vouch
2
3import (
4 "strconv"
5
6 "gno.land/p/moul/kit/num/v0"
7 "gno.land/p/moul/kit/ui/v0"
8 "gno.land/p/moul/md/v0"
9 "gno.land/p/moul/realmpath/v0"
10 vo "gno.land/p/moul/x/social/vouch/v0"
11)
12
13// Render routes two views: the realm's own index, and one address's page.
14//
15// An address carries no slash, so the per-address route is a path element and
16// not a prefix strip.
17func Render(path string) string {
18 req := realmpath.Parse(path)
19
20 switch req.PathPart(0) {
21 case "":
22 return renderIndex()
23 case "addr":
24 return renderAddr(req.PathPart(1))
25 default:
26 return md.H1("Not found") + "No such view: " + ui.Inline(req.Path)
27 }
28}
29
30func renderIndex() string {
31 out := md.H1("Vouch")
32 out += "A web of trust other realms gate on. One address vouches for another, " +
33 "in writing, optionally with GNOT locked behind it.\n\n"
34
35 t := ui.NewTable("vouches", "people vouched for", "bonded", "awaiting withdrawal")
36 t.Row(
37 strconv.Itoa(graph.Count()),
38 strconv.Itoa(graph.People()),
39 num.GNOTf(graph.TotalBonded()),
40 num.GNOTf(graph.TotalOwed()),
41 )
42 out += t.String()
43
44 out += md.H2("Most vouched for")
45 out += board()
46
47 out += md.H2("The gate")
48 out += "One line in the realm that wants it, and it costs no transaction:\n\n"
49 out += md.LanguageCodeBlock("go", `import "`+realmPath+`"
50
51if !vouch.IsTrusted(addr, 2) {
52 panic("get two people to vouch for you first")
53}`)
54 out += md.Italic("A score counts distinct vouchers, so restating a vouch does not " +
55 "raise it and revoking lowers it. It does not count distinct people: a pair " +
56 "vouching for each other both reach one, which is why a real gate asks for " +
57 "more.") + "\n\n"
58
59 out += md.H2("Take part")
60 out += ui.Action("\U0001F91D Vouch", "Vouch", "target", "", "reason", "") +
61 " · " + ui.Action("\U0001F4B0 Withdraw a refunded bond", "Withdraw") + "\n\n"
62 out += md.Italic("Coins sent with Vouch are locked as a bond on that vouch and come " +
63 "back when you revoke it. There is no slashing: deciding that a vouch was a lie " +
64 "needs an arbiter, and choosing one is the whole design question.") + "\n"
65
66 return out
67}
68
69func board() string {
70 rows := graph.Leaderboard(BoardSize)
71 if len(rows) == 0 {
72 return ui.Empty("Nobody has been vouched for yet.")
73 }
74 t := ui.NewTable("", "address", "vouchers", "bonded")
75 for i, r := range rows {
76 t.Row(
77 ui.Podium(i),
78 md.Link(ui.AddrText(r.Addr), vo.AddrURL(realmPath, r.Addr)),
79 strconv.Itoa(r.Score),
80 num.GNOTf(r.Bonded),
81 )
82 }
83 return t.String()
84}
85
86func renderAddr(raw string) string {
87 addr := address(raw)
88 if !addr.IsValid() {
89 return md.H1("Vouch") + "Not an address: " + ui.Inline(raw)
90 }
91
92 out := md.H1("Vouch for " + ui.AddrText(addr))
93 out += ui.AddrFull(addr) + "\n\n"
94
95 score := graph.ScoreOf(addr)
96 t := ui.NewTable("vouchers", "bonded on them", "trusted at 2", "awaiting withdrawal")
97 t.Row(
98 strconv.Itoa(score),
99 num.GNOTf(graph.BondedFor(addr)),
100 yesno(graph.IsTrusted(addr, 2)),
101 num.GNOTf(graph.Owed(addr)),
102 )
103 out += t.String()
104
105 out += md.H2("Vouched for by")
106 out += inbound(addr)
107
108 out += md.H2("Vouches for")
109 out += outbound(addr)
110
111 out += "\n" + ui.Action("\U0001F91D Vouch for this address", "Vouch",
112 "target", addr.String(), "reason", "") +
113 " · " + ui.Action("\U0000274C Revoke mine", "Revoke", "target", addr.String()) + "\n"
114 return out
115}
116
117func inbound(addr address) string {
118 from := graph.VouchedBy(addr)
119 if len(from) == 0 {
120 return ui.Empty("Nobody vouches for this address.")
121 }
122 t := ui.NewTable("voucher", "reason", "bond", "mutual", "since")
123 for _, f := range from {
124 v, ok := graph.Get(f, addr)
125 if !ok {
126 continue
127 }
128 t.Row(
129 md.Link(ui.AddrText(f), vo.AddrURL(realmPath, f)),
130 ui.Cell(v.Reason),
131 num.GNOTf(v.Bond),
132 yesno(graph.Mutual(f, addr)),
133 strconv.FormatInt(v.At, 10),
134 )
135 }
136 return t.String()
137}
138
139func outbound(addr address) string {
140 to := graph.VouchesOf(addr)
141 if len(to) == 0 {
142 return ui.Empty("This address vouches for nobody.")
143 }
144 t := ui.NewTable("for", "reason", "bond", "their vouchers")
145 for _, target := range to {
146 v, ok := graph.Get(addr, target)
147 if !ok {
148 continue
149 }
150 t.Row(
151 md.Link(ui.AddrText(target), vo.AddrURL(realmPath, target)),
152 ui.Cell(v.Reason),
153 num.GNOTf(v.Bond),
154 strconv.Itoa(graph.ScoreOf(target)),
155 )
156 }
157 return t.String()
158}
159
160func yesno(b bool) string {
161 if b {
162 return "yes"
163 }
164 return "no"
165}