render.gno
2.97 Kb · 90 lines
1package sharedpoll
2
3import (
4 "strconv"
5 "strings"
6 "time"
7
8 "gno.land/p/g1lnkytfqcjwllws63gvf0mv9yt04aswy4y9amhm/remixlab/core/v1"
9 "gno.land/p/g1lnkytfqcjwllws63gvf0mv9yt04aswy4y9amhm/remixlab/render/v1"
10)
11
12// Render serves the overview, the poll list and single polls.
13func Render(path string) string {
14 if admins == nil {
15 return render.Heading(1, "Shared-membership poll") + "This app is not initialized.\n"
16 }
17 segs, q := render.ParsePath(path)
18 switch {
19 case len(segs) == 0:
20 return renderHome()
21 case segs[0] == "polls" && len(segs) == 1:
22 pages := render.PageCount(polls.Count(), pageSize)
23 page := render.Page(q, pages)
24 return render.Heading(1, "Polls") +
25 renderPollTable(polls.List((page-1)*pageSize, pageSize)) +
26 render.Pager(pkgPath, "polls", page, pages)
27 case segs[0] == "polls" && len(segs) == 2:
28 id, err := strconv.ParseUint(segs[1], 10, 64)
29 if err == nil {
30 if v, ok := polls.Ballot(id); ok {
31 return renderPoll(v)
32 }
33 }
34 }
35 return render.Heading(1, "Not found") + render.Link("Back", render.RealmURL(pkgPath, "")) + "\n"
36}
37
38func renderHome() string {
39 var sb strings.Builder
40 sb.WriteString(render.Heading(1, "Shared-membership poll"))
41 sb.WriteString("Voters come from " + render.Link(sharedPath, render.RealmURL(sharedPath, "")) +
42 ", snapshotted when each poll opens. Join or leave there.\n\n")
43 sb.WriteString(renderPollTable(polls.List(0, 5)))
44 sb.WriteString(render.Link("All polls", render.RealmURL(pkgPath, "polls")) + "\n")
45 return sb.String()
46}
47
48func renderPollTable(list []core.BallotView) string {
49 if len(list) == 0 {
50 return "No polls yet.\n\n"
51 }
52 var rows [][]string
53 for _, v := range list {
54 id := strconv.FormatUint(v.ID, 10)
55 rows = append(rows, render.TrustedRow(
56 render.Link("#"+id, render.RealmURL(pkgPath, "polls/"+id)),
57 render.Text(v.Title),
58 render.Text(v.Status.String()),
59 strconv.Itoa(v.Turnout),
60 ))
61 }
62 return render.Table([]string{"Poll", "Title", "Status", "Ballots"}, rows)
63}
64
65func renderPoll(v core.BallotView) string {
66 now := time.Now().Unix()
67 id := strconv.FormatUint(v.ID, 10)
68 var sb strings.Builder
69 sb.WriteString(render.Heading(1, v.Title))
70 sb.WriteString("Status: **" + render.Text(v.Status.String()) + "** · electorate " + strconv.Itoa(v.ElectorateSize) + "\n\n")
71 var rows [][]string
72 for i, opt := range v.Options {
73 action := ""
74 if v.Status == core.BallotOpen && now < v.Deadline {
75 action = render.TxLink("vote", pkgPath, "Vote", "id", id, "choice", strconv.Itoa(i))
76 }
77 rows = append(rows, render.TrustedRow(render.Text(opt), strconv.Itoa(v.Counts[i]), action))
78 }
79 sb.WriteString(render.Table([]string{"Option", "Votes", ""}, rows))
80 if v.Status == core.BallotFinalized {
81 res := v.Outcome.String()
82 if v.Outcome == core.OutcomeWinner {
83 res = "winner: " + v.Options[v.Winner]
84 }
85 sb.WriteString("Result: **" + render.Text(res) + "**\n")
86 } else if v.Status == core.BallotOpen && now >= v.Deadline {
87 sb.WriteString(render.TxLink("Finalize result", pkgPath, "Finalize", "id", id) + "\n")
88 }
89 return sb.String()
90}