render_board.gno
4.70 Kb · 186 lines
1package boards2
2
3import (
4 "strings"
5
6 "gno.land/p/gnoland/boards"
7 "gno.land/p/jeronimoalbi/mdform"
8 "gno.land/p/jeronimoalbi/pager"
9 "gno.land/p/moul/md"
10 "gno.land/p/nt/mdalert/v0"
11 "gno.land/p/nt/mux/v0"
12)
13
14func renderBoard(res *mux.ResponseWriter, req *mux.Request) {
15 name := req.GetVar("board")
16 board, found := gBoards.GetByName(name)
17 if !found {
18 res.Write(md.H3("The board you are looking for does not exist"))
19 res.Write("Do you want to " + md.Link("create a new board", createBoardURI) + "?")
20 return
21 }
22
23 creatorLink := userLink(board.Creator)
24 date := board.CreatedAt.Format(dateFormat)
25
26 res.Write(md.H1(md.Link("Boards", gRealmPath) + " › " + board.Name))
27 if board.Readonly {
28 res.Write(
29 mdalert.Warning("Info", "Creating new threads and commenting are disabled within this board") + "\n",
30 )
31 }
32
33 res.Write("Created by " + creatorLink + " on " + date + ", #" + board.ID.String())
34 res.Write(" \n" + renderBoardMenu(board, req))
35 res.Write(md.HorizontalRule())
36
37 if board.Threads.Size() == 0 {
38 res.Write(md.H3("This board doesn't have any threads"))
39 if !board.Readonly {
40 startConversationLink := md.Link("start a new conversation", makeCreateThreadURI(board))
41 res.Write("Do you want to " + startConversationLink + " in this board?")
42 }
43 return
44 }
45
46 p, err := pager.New(req.RawPath, board.Threads.Size(), pager.WithPageSize(pageSizeDefault))
47 if err != nil {
48 panic(err)
49 }
50
51 render := func(thread *boards.Post) bool {
52 res.Write(renderThreadSummary(thread) + "\n")
53 return false
54 }
55
56 res.Write("Sort by: ")
57
58 r := parseRealmPath(req.RawPath)
59 sortOrder := r.Query.Get("order")
60 if sortOrder == "desc" {
61 r.Query.Set("order", "asc")
62 res.Write(md.Link("newest first", r.String()) + "\n\n")
63 } else {
64 r.Query.Set("order", "desc")
65 res.Write(md.Link("oldest first", r.String()) + "\n\n")
66 }
67
68 count := p.PageSize()
69 if sortOrder == "desc" {
70 count = -count // Reverse iterate
71 }
72
73 board.Threads.Iterate(p.Offset(), count, render)
74
75 if p.HasPages() {
76 res.Write(md.HorizontalRule())
77 res.Write(pager.Picker(p))
78 }
79}
80
81// renderSubMenu renders a sub-menu with a distinct visual pattern.
82func renderSubMenu(items []string) string {
83 if len(items) == 0 {
84 return ""
85 }
86 return "└─ " + strings.Join(items, " • ") + "\n"
87}
88
89func renderBoardMenu(board *boards.Board, req *mux.Request) string {
90 var (
91 b strings.Builder
92 boardMembersURL = makeBoardURI(board) + "/members"
93 )
94
95 if board.Readonly {
96 b.WriteString(md.Link("List Members", boardMembersURL))
97 b.WriteString(" • ")
98 b.WriteString(md.Link("Unfreeze Board", makeUnfreezeBoardURI(board)))
99 b.WriteString("\n")
100 } else {
101 b.WriteString("↳ ")
102 b.WriteString(md.Link("Create Thread", makeCreateThreadURI(board)))
103 b.WriteString(" • ")
104 b.WriteString(md.Link("Request Invite", makeRequestInviteURI(board)))
105 b.WriteString(" • ")
106
107 menu := getCurrentMenu(req.RawPath)
108 if menu == menuManageBoard {
109 b.WriteString(md.Bold("Manage Board"))
110 } else {
111 b.WriteString(md.Link("Manage Board", menuURL(menuManageBoard)))
112 }
113
114 b.WriteString(" \n")
115
116 if menu == menuManageBoard {
117 subMenuItems := []string{
118 md.Link("Invite Member", makeInviteMemberURI(board)),
119 md.Link("List Invite Requests", makeBoardURI(board)+"/invites"),
120 md.Link("List Members", boardMembersURL),
121 md.Link("List Banned Users", makeBoardURI(board)+"/banned-users"),
122 md.Link("Freeze Board", makeFreezeBoardURI(board)),
123 }
124 b.WriteString(renderSubMenu(subMenuItems))
125 }
126 }
127
128 b.WriteString("\n")
129 return b.String()
130}
131
132func renderInviteMember(res *mux.ResponseWriter, req *mux.Request) {
133 name := req.GetVar("board")
134 board, found := gBoards.GetByName(name)
135 if !found {
136 res.Write("Board not found")
137 return
138 }
139
140 form := mdform.New("exec", "InviteMember")
141 form.Input(
142 "boardID",
143 "placeholder", "Board ID",
144 "value", board.ID.String(),
145 "readonly", "true",
146 )
147 form.Input(
148 "user",
149 "placeholder", "Address",
150 "required", "true",
151 )
152 form.Select(
153 "role",
154 string(RoleOwner),
155 )
156 form.Select(
157 "role",
158 string(RoleAdmin),
159 )
160 form.Select(
161 "role",
162 string(RoleModerator),
163 )
164 form.Select(
165 "role",
166 string(RoleGuest),
167 "selected", "true",
168 )
169
170 res.Write(md.H1(board.Name + ": Invite Member"))
171 res.Write(
172 md.Paragraph(
173 "Both open and invite only boards can have multiple members with different roles within a "+
174 "board, where members can have a single role at a time.",
175 ) +
176 md.Paragraph(
177 "Boards are independent communities which could apply different permissions per role than "+
178 "other boards, but generally Boards2 supports four roles, _owner_, _admin_, _moderator_ "+
179 "and _guest_.",
180 ) +
181 md.Paragraph(
182 "Member will be added to "+md.Link(board.Name, makeBoardURI(board))+" board.",
183 ),
184 )
185 res.Write(form.String())
186}