render.gno
10.36 Kb ยท 328 lines
1package basedao
2
3import (
4 "strconv"
5 "strings"
6
7 "gno.land/p/nt/mux"
8 "gno.land/p/nt/seqid"
9 "gno.land/p/nt/ufmt"
10 "gno.land/p/samcrew/daokit"
11)
12
13// WELL KNOWN PATHS
14const (
15 HOME_PATH = ""
16 HOME_NO_PROFILE_PATH = "noprofile"
17 CONFIG_PATH = "config"
18 PROPOSAL_HISTORY_PATH = "history"
19 MEMBER_DETAIL_PATH = "member/{address}"
20 PROPOSAL_DETAIL_PATH = "proposal/{id}"
21 FALLBACK_DISPLAY_NAME = "Anon"
22)
23
24func (d *DAOPrivate) initRenderingRouter() {
25 if d.RenderRouter == nil {
26 d.RenderRouter = mux.NewRouter()
27 }
28}
29
30func (d *DAOPrivate) InitDefaultRendering() {
31 d.RenderRouter.HandleFunc(HOME_PATH, d.MuxHomePage)
32 d.RenderRouter.HandleFunc(HOME_NO_PROFILE_PATH, d.MuxHomePage)
33 d.RenderRouter.HandleFunc(CONFIG_PATH, d.MuxConfigPage)
34 d.RenderRouter.HandleFunc(PROPOSAL_HISTORY_PATH, d.MuxProposalHistoryPage)
35 d.RenderRouter.HandleFunc(MEMBER_DETAIL_PATH, d.MuxMemberDetailPage)
36 d.RenderRouter.HandleFunc(PROPOSAL_DETAIL_PATH, d.MuxProposalDetailPage)
37}
38
39func (d *DAOPrivate) Render(path string) string {
40 return d.RenderRouter.Render(path)
41}
42
43// DEFAULT_HANDLERS
44
45func (d *DAOPrivate) MuxHomePage(res *mux.ResponseWriter, req *mux.Request) {
46 res.Write(d.HomePageView())
47}
48
49func (d *DAOPrivate) MuxConfigPage(res *mux.ResponseWriter, req *mux.Request) {
50 res.Write(d.ConfigPageView())
51}
52
53func (d *DAOPrivate) MuxProposalHistoryPage(res *mux.ResponseWriter, req *mux.Request) {
54 res.Write(d.ProposalHistoryPageView())
55}
56
57func (d *DAOPrivate) MuxMemberDetailPage(res *mux.ResponseWriter, req *mux.Request) {
58 res.Write(d.MemberDetailPageView(req.GetVar("address")))
59}
60
61func (d *DAOPrivate) MuxProposalDetailPage(res *mux.ResponseWriter, req *mux.Request) {
62 id, err := strconv.ParseUint(req.GetVar("id"), 10, 64)
63 if err != nil {
64 panic(err)
65 }
66 res.Write(d.ProposalDetailPageView(id))
67}
68
69// PUBLIC PAGES
70
71func (d *DAOPrivate) HomePageView() string {
72 return d.HomeProfileHeaderView(false) +
73 d.HomeMembersView() +
74 d.HomeProposalsView()
75}
76
77func (d *DAOPrivate) ConfigPageView() string {
78 return d.ConfigHeaderView() +
79 d.ConfigRolesView() +
80 d.ConfigResourcesView()
81}
82
83func (d *DAOPrivate) ProposalHistoryPageView() string {
84 return d.ProposalHistoryHeaderView() +
85 d.ProposalHistoryView()
86}
87
88func (d *DAOPrivate) MemberDetailPageView(address string) string {
89 return d.MemberDetailHeaderView() +
90 d.MemberDetailView(address)
91}
92
93func (d *DAOPrivate) ProposalDetailPageView(idu uint64) string {
94 return d.ProposalDetailHeaderView() +
95 d.ProposalDetailView(idu)
96}
97
98// --- PUBLIC COMPONENT VIEWS ---
99
100// HOME PAGE COMPONENTS VIEWS
101
102func (d *DAOPrivate) HomeProfileHeaderView(noprofile bool) string {
103 s := ""
104 name := d.GetProfileString(d.Realm.Address(), "DisplayName", "DAO")
105 description := d.GetProfileString(d.Realm.Address(), "Bio", "Created with daokit")
106 pkgPath := d.Realm.PkgPath()
107 linkPath := getLinkPath(pkgPath)
108
109 if !noprofile {
110 s += ufmt.Sprintf("# %s\n\n", name)
111 imageURI := d.GetProfileString(d.Realm.Address(), "Avatar", "")
112 if imageURI != "" {
113 s += ufmt.Sprintf("\n\n", imageURI)
114 }
115 s += ufmt.Sprintf("%s\n\n", description)
116 }
117
118 s += ufmt.Sprintf("> Realm address: %s\n\n", d.Realm.Address())
119 s += ufmt.Sprintf("Discover more about this DAO on the [configuration page โ๏ธ](%s:%s)\n\n", linkPath, CONFIG_PATH)
120 s += ufmt.Sprintf("\n--------------------------------\n")
121
122 return s
123}
124
125func (d *DAOPrivate) HomeMembersView() string {
126 pkgPath := d.Realm.PkgPath()
127 linkPath := getLinkPath(pkgPath)
128 s := ""
129 s += ufmt.Sprintf("## Members ๐ค \n\n")
130 i := 1
131 d.Members.Members.Iterate("", "", func(key string, value interface{}) bool {
132 s += ufmt.Sprintf("- **Member %d: [%s](%s:%s/%s)**\n\n", i, key, linkPath, "member", key)
133 s += ufmt.Sprintf(" - **Profile:** %s\n", d.GetProfileString(address(key), "DisplayName", FALLBACK_DISPLAY_NAME))
134 s += ufmt.Sprintf(" - **Roles:** %s\n\n", strings.Join(d.Members.GetMemberRoles(key), ", "))
135 i += 1
136 return false
137 })
138 s += ufmt.Sprintf("> You can find more information about a member by clicking on their address\n\n")
139 s += ufmt.Sprintf("\n--------------------------------\n")
140 return s
141}
142
143func (d *DAOPrivate) HomeProposalsView() string {
144 pkgPath := d.Realm.PkgPath()
145 linkPath := getLinkPath(pkgPath)
146 s := ""
147 s += ufmt.Sprintf("## Proposals ๐ณ๏ธ \n\n")
148 i := 0
149 d.Core.Proposals.Tree.Iterate("", "", func(key string, value interface{}) bool {
150 proposal := value.(*daokit.Proposal)
151 if proposal.Status != daokit.ProposalStatusOpen {
152 return false
153 }
154 id, err := seqid.FromString(key)
155 if err != nil {
156 panic(err)
157 }
158 s += ufmt.Sprintf("- **Proposal %d: [%s](%s:%s/%d)**\n\n", uint64(id), proposal.Title, linkPath, "proposal", uint64(id))
159 i += 1
160 return false
161 })
162 if i == 0 {
163 s += ufmt.Sprintf("\tโ ๏ธ There are no running proposals at the moment\n\n")
164 }
165 s += ufmt.Sprintf("> See the [proposal history ๐](%s:%s) for more information\n\n", linkPath, PROPOSAL_HISTORY_PATH)
166 s += ufmt.Sprintf("\n--------------------------------\n")
167 return s
168}
169
170// CONFIG PAGE COMPONENTS VIEWS
171
172func (d *DAOPrivate) ConfigHeaderView() string {
173 name := d.GetProfileString(d.Realm.Address(), "DisplayName", "DAO")
174 s := ""
175 s += ufmt.Sprintf("# %s - Config โ๏ธ\n\n", name)
176 s += ufmt.Sprintf("\n--------------------------------\n")
177 return s
178}
179
180func (d *DAOPrivate) ConfigRolesView() string {
181 roles := d.Members.GetRoles()
182 s := ""
183 s += ufmt.Sprintf("## Roles ๐ท๏ธ\n\n")
184 for _, role := range roles {
185 s += ufmt.Sprintf("- %s\n\n", role)
186 info := d.Members.RoleInfo(role)
187 s += ufmt.Sprintf(" %s\n\n", info.Description)
188 }
189 s += ufmt.Sprintf("\n--------------------------------\n")
190 return s
191}
192
193func (d *DAOPrivate) ConfigResourcesView() string {
194 s := ""
195 s += ufmt.Sprintf("## Resources ๐ฆ\n\n")
196 i := 1
197 d.Core.Resources.Tree.Iterate("", "", func(key string, value interface{}) bool {
198 resource := value.(*daokit.Resource)
199 s += ufmt.Sprintf("- **Resource #%d: %s**\n\n", i, key)
200 // TODO: add doc to handler and print here
201 s += ufmt.Sprintf(" - **Name:** %s\n", resource.DisplayName)
202 s += ufmt.Sprintf(" - **Description:** %s\n", resource.Description)
203 s += ufmt.Sprintf(" - **Condition:** %s\n\n", resource.Condition.Render())
204 i += 1
205 return false
206 })
207 s += ufmt.Sprintf("\n--------------------------------\n")
208 return s
209}
210
211// HISTORY PAGE COMPONENTS VIEWS
212
213func (d *DAOPrivate) ProposalHistoryHeaderView() string {
214 name := d.GetProfileString(d.Realm.Address(), "DisplayName", "DAO")
215 s := ""
216 s += ufmt.Sprintf("# %s - Proposal History\n\n", name)
217 s += ufmt.Sprintf("\n--------------------------------\n")
218 return s
219}
220
221func (d *DAOPrivate) ProposalHistoryView() string {
222 pkgPath := d.Realm.PkgPath()
223 linkPath := getLinkPath(pkgPath)
224 s := ""
225 s += ufmt.Sprintf("## Proposals ๐ณ๏ธ\n\n")
226 i := 1
227 d.Core.Proposals.Tree.Iterate("", "", func(key string, value interface{}) bool {
228 proposal, ok := value.(*daokit.Proposal)
229 if !ok {
230 panic("unexpected invalid proposal type")
231 }
232 id, err := seqid.FromString(key)
233 if err != nil {
234 panic(err)
235 }
236 s += ufmt.Sprintf("- **Proposal %d: [%s](%s:%s/%d) - %s**\n\n", uint64(id), proposal.Title, linkPath, "proposal", uint64(id), proposal.Status)
237 i += 1
238 return false
239 })
240 s += ufmt.Sprintf("[Add a new proposal ๐ณ๏ธ](%s$help)\n\n", linkPath)
241 s += ufmt.Sprintf("\n--------------------------------\n")
242 return s
243}
244
245// MEMBER DETAIL PAGE COMPONENTS VIEWS
246
247func (d *DAOPrivate) MemberDetailHeaderView() string {
248 name := d.GetProfileString(d.Realm.Address(), "DisplayName", "DAO")
249 s := ""
250 s += ufmt.Sprintf("# %s - Member Detail\n\n", name)
251 s += ufmt.Sprintf("\n--------------------------------\n")
252 return s
253}
254
255func (d *DAOPrivate) MemberDetailView(addr string) string {
256 pkgPath := d.Realm.PkgPath()
257 linkPath := getLinkPath(pkgPath)
258 roles := d.Members.GetMemberRoles(addr)
259 displayName := d.GetProfileString(address(addr), "DisplayName", FALLBACK_DISPLAY_NAME)
260 bio := d.GetProfileString(address(addr), "Bio", "No bio")
261 pp := d.GetProfileString(address(addr), "Avatar", "")
262 s := ""
263 s += ufmt.Sprintf("## Profile ๐ค\n\n")
264 s += ufmt.Sprintf("- **Display Name:** %s\n\n", displayName)
265 s += ufmt.Sprintf("- **Bio:** %s\n\n", bio)
266 if pp != "" {
267 s += ufmt.Sprintf("\n\n", pp)
268 }
269 s += ufmt.Sprintf("## Roles ๐ท๏ธ\n\n")
270 for _, role := range roles {
271 s += ufmt.Sprintf("- %s\n\n", role)
272 }
273 s += ufmt.Sprintf("> Learn more about the roles on the [configuration page โ๏ธ](%s:%s)\n\n", linkPath, CONFIG_PATH)
274 s += ufmt.Sprintf("\n--------------------------------\n")
275 return s
276}
277
278// PROPOSAL DETAIL PAGE COMPONENTS VIEWS
279
280func (d *DAOPrivate) ProposalDetailHeaderView() string {
281 name := d.GetProfileString(d.Realm.Address(), "DisplayName", "DAO")
282 s := ""
283 s += ufmt.Sprintf("# %s - Proposal Detail\n\n", name)
284 s += ufmt.Sprintf("\n--------------------------------\n")
285 return s
286}
287
288func (d *DAOPrivate) ProposalDetailView(idu uint64) string {
289 pkgPath := d.Realm.PkgPath()
290 linkPath := getLinkPath(pkgPath)
291 id := seqid.ID(idu)
292 proposal := d.Core.Proposals.GetProposal(uint64(id))
293 s := ""
294 s += ufmt.Sprintf("## Title - %s ๐\n\n", proposal.Title)
295 s += ufmt.Sprintf("## Description ๐\n\n%s\n\n", proposal.Description)
296 s += ufmt.Sprintf("## Resource - %s ๐ฆ\n\n", proposal.Action.Type())
297 resource := d.Core.Resources.Get(proposal.Action.Type())
298 s += ufmt.Sprintf(" - **Name:** %s\n", resource.DisplayName)
299 s += ufmt.Sprintf(" - **Description:** %s\n", resource.Description)
300 s += ufmt.Sprintf(" - **Condition:** %s\n\n", resource.Condition.Render())
301 s += ("---\n\n")
302 s += proposal.Action.String() + "\n\n"
303 s += ("---\n\n")
304 if proposal.Status == daokit.ProposalStatusOpen {
305 s += ufmt.Sprintf("## Status - Open ๐ก\n\n")
306 s += ufmt.Sprintf("[Vote on this proposal ๐ณ๏ธ](%s$help)\n\n", linkPath)
307 } else if proposal.Status == daokit.ProposalStatusPassed {
308 s += ufmt.Sprintf("## Status - Passed ๐ข\n\n")
309 s += ufmt.Sprintf("[Execute this proposal ๐ณ๏ธ](%s$help)\n\n", linkPath)
310 } else if proposal.Status == daokit.ProposalStatusExecuted {
311 s += ufmt.Sprintf("## Status - Executed โ
\n\n")
312 } else {
313 s += ufmt.Sprintf("## Status - Closed ๐ด\n\n")
314 }
315 s += ufmt.Sprintf("> proposed by %s ๐ค\n\n", proposal.ProposerID)
316 s += ufmt.Sprintf("\n--------------------------------\n")
317 s += ufmt.Sprintf("## Votes ๐ณ๏ธ\n\n%s\n\n", proposal.Condition.RenderWithVotes(proposal.Ballot))
318 s += ufmt.Sprintf("\n--------------------------------\n")
319 return s
320}
321
322func getLinkPath(pkgPath string) string {
323 slashIdx := strings.IndexRune(pkgPath, '/')
324 if slashIdx != 1 {
325 return pkgPath[slashIdx:]
326 }
327 return ""
328}