render.gno
4.23 Kb ยท 169 lines
1package impl
2
3import (
4 "chain/runtime"
5 "strconv"
6 "strings"
7
8 "gno.land/p/moul/helplink"
9 "gno.land/p/nt/avl/pager"
10 "gno.land/p/nt/mux"
11 "gno.land/p/nt/seqid"
12 "gno.land/p/nt/ufmt"
13 "gno.land/r/gov/dao"
14 "gno.land/r/sys/users"
15)
16
17type render struct {
18 relativeRealmPath string
19 router *mux.Router
20 pssPager *pager.Pager
21}
22
23func NewRender(d *GovDAO) *render {
24 ren := &render{
25 pssPager: pager.NewPager(d.pss.Tree, 5, true),
26 }
27
28 r := mux.NewRouter()
29
30 r.HandleFunc("", func(rw *mux.ResponseWriter, req *mux.Request) {
31 rw.Write(ren.renderActiveProposals(req.RawPath, d))
32 })
33
34 r.HandleFunc("{pid}", func(rw *mux.ResponseWriter, req *mux.Request) {
35 rw.Write(ren.renderProposalPage(req.GetVar("pid"), d))
36 })
37
38 r.HandleFunc("{pid}/votes", func(rw *mux.ResponseWriter, req *mux.Request) {
39 rw.Write(ren.renderVotesForProposal(req.GetVar("pid"), d))
40 })
41
42 ren.router = r
43
44 return ren
45}
46
47func (ren *render) Render(pkgPath string, path string) string {
48 relativePath, found := strings.CutPrefix(pkgPath, runtime.ChainDomain())
49 if !found {
50 panic(ufmt.Sprintf(
51 "realm package with unexpected name found: %v in chain domain %v",
52 pkgPath, runtime.ChainDomain()))
53 }
54 ren.relativeRealmPath = relativePath
55 return ren.router.Render(path)
56}
57
58func (ren *render) renderActiveProposals(url string, d *GovDAO) string {
59 out := "# GovDAO\n"
60 out += "## Members\n"
61 out += "[> Go to Memberstore <](/r/gov/dao/v3/memberstore)\n"
62 out += "## Proposals\n"
63 page := ren.pssPager.MustGetPageByPath(url)
64 for _, item := range page.Items {
65 seqpid, err := seqid.FromString(item.Key)
66 if err != nil {
67 panic(err.Error())
68 }
69 out += ren.renderProposalListItem(ufmt.Sprintf("%v", int64(seqpid)), d)
70 out += "---\n\n"
71 }
72
73 out += page.Picker("")
74
75 return out
76}
77
78func (ren *render) renderProposalPage(sPid string, d *GovDAO) string {
79 pid, err := strconv.ParseInt(sPid, 10, 64)
80 if err != nil {
81 panic(err.Error())
82 }
83 ps := d.pss.GetStatus(dao.ProposalID(pid))
84 p := dao.MustGetProposal(cross, dao.ProposalID(pid))
85 out := ufmt.Sprintf("## Prop #%v - %v\n", pid, p.Title())
86 out += "Author: " + tryResolveAddr(p.Author()) + "\n\n"
87
88 out += p.Description()
89 out += "\n\n"
90
91 out += "\n\n---\n\n"
92 out += ps.String()
93 out += "\n"
94 out += ufmt.Sprintf("[Detailed voting list](%v:%v/votes)", ren.relativeRealmPath, pid)
95 out += "\n\n---\n\n"
96
97 out += renderActionBar(ufmt.Sprintf("%v", pid))
98
99 return out
100}
101
102func (ren *render) renderProposalListItem(sPid string, d *GovDAO) string {
103 pid, err := strconv.ParseInt(sPid, 10, 64)
104 if err != nil {
105 panic(err.Error())
106 }
107 ps := d.pss.GetStatus(dao.ProposalID(pid))
108 p := dao.MustGetProposal(cross, dao.ProposalID(pid))
109 out := ufmt.Sprintf("### [Prop #%v - %v](%v:%v)\n", pid, p.Title(), ren.relativeRealmPath, pid)
110 out += ufmt.Sprintf("Author: %s\n\n", tryResolveAddr(p.Author()))
111
112 out += "Status: " + getPropStatus(ps)
113 out += "\n\n"
114
115 out += "Tiers eligible to vote: "
116 out += strings.Join(ps.TiersAllowedToVote, ", ")
117
118 out += "\n\n"
119 return out
120}
121
122func (ren *render) renderVotesForProposal(sPid string, d *GovDAO) string {
123 pid, err := strconv.ParseInt(sPid, 10, 64)
124 if err != nil {
125 panic(err.Error())
126 }
127 ps := d.pss.GetStatus(dao.ProposalID(pid))
128
129 out := ""
130 out += ufmt.Sprintf("# Proposal #%v - Vote List\n\n", pid)
131 out += StringifyVotes(ps)
132
133 return out
134}
135
136func isPropActive(ps *proposalStatus) bool {
137 return !ps.Accepted && !ps.Denied
138}
139
140func getPropStatus(ps *proposalStatus) string {
141 if ps.Accepted {
142 return "ACCEPTED"
143 } else if ps.Denied {
144 return "REJECTED"
145 }
146 return "ACTIVE"
147}
148
149func renderActionBar(sPid string) string {
150 out := "### Actions\n"
151
152 proxy := helplink.Realm("gno.land/r/gov/dao")
153 out += proxy.Func("Vote YES", "MustVoteOnProposalSimple", "pid", sPid, "option", "YES") + " | "
154 out += proxy.Func("Vote NO", "MustVoteOnProposalSimple", "pid", sPid, "option", "NO") + " | "
155 out += proxy.Func("Vote ABSTAIN", "MustVoteOnProposalSimple", "pid", sPid, "option", "ABSTAIN")
156
157 out += "\n\n"
158 out += "WARNING: Please double check transaction data before voting.\n\n"
159 out += "**New**: Members **must** have a namespace to vote to enforce simpler tracking."
160 return out
161}
162
163func tryResolveAddr(addr address) string {
164 userData := users.ResolveAddress(addr)
165 if userData == nil {
166 return addr.String()
167 }
168 return userData.RenderLink("")
169}