Skip to content

Commit

Permalink
feat(boards2): improve menus and add missing actions (#3809)
Browse files Browse the repository at this point in the history
PR add missing options that link to Gnoweb help forms to allow easy
generation of `gnokey` commands.

It also introduces new menus using a simplistic Markdown to simulate
sub-menus to avoid extensive menu entries that require specific
permissions to execute.

Board List Menu:
<img width="429" alt="Screenshot 2025-02-23 at 18 40 44"
src="https://github.com/user-attachments/assets/2118572f-254b-4cf9-95de-a3453df70eb8"
/>

Board Menu:
<img width="413" alt="Screenshot 2025-02-23 at 18 40 57"
src="https://github.com/user-attachments/assets/2c3bcd08-4bfa-4292-8329-588596ec3c91"
/>

<img width="443" alt="Screenshot 2025-02-23 at 18 41 05"
src="https://github.com/user-attachments/assets/9b5e4990-4fa7-4d65-a98b-082b7295c7fd"
/>
  • Loading branch information
jeronimoalbi authored Feb 24, 2025
1 parent c80653b commit 47347a5
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 12 deletions.
29 changes: 25 additions & 4 deletions examples/gno.land/r/nt/boards2/v1/board.gno
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,7 @@ func (board *Board) DeleteThread(pid PostID) {
//
// Pager is used for pagination if it's not nil.
func (board *Board) Render(p *PaginationOpts) string {
sb := &strings.Builder{}

sb.WriteString(newButtonLink("post", board.GetPostFormURL()))
sb.WriteString("\n\n")
var sb strings.Builder

if board.threads.Size() == 0 {
sb.WriteString("*This board doesn't have any threads.*")
Expand Down Expand Up @@ -148,6 +145,30 @@ func (board *Board) GetURLFromReplyID(threadID, replyID PostID) string {
return board.GetURL() + "/" + threadID.String() + "/" + replyID.String()
}

func (board *Board) GetRenameFormURL() string {
return txlink.Call("RenameBoard", "name", board.name)
}

func (board *Board) GetFreezeFormURL() string {
return txlink.Call("FreezeBoard", "boardID", board.id.String())
}

func (board *Board) GetFlaggingThresholdFormURL() string {
return txlink.Call("SetFlaggingThreshold", "boardID", board.id.String())
}

func (board *Board) GetInviteMemberFormURL() string {
return txlink.Call("InviteMember", "boardID", board.id.String())
}

func (board *Board) GetRemoveMemberFormURL() string {
return txlink.Call("RemoveMember", "boardID", board.id.String())
}

func (board *Board) GetChangeMemberRoleFormURL() string {
return txlink.Call("ChangeMemberRole", "boardID", board.id.String())
}

func (board *Board) GetPostFormURL() string {
return txlink.Call("CreateThread", "boardID", board.id.String())
}
Expand Down
5 changes: 4 additions & 1 deletion examples/gno.land/r/nt/boards2/v1/pagination.gno
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ func (opts *PaginationOpts) Iterate(tree *avl.Tree, cb func(k string, val interf
}
}

return page
if page.TotalPages > 1 {
return page
}
return nil
}

func mustGetPagination(rawPath string, pageSize int) *PaginationOpts {
Expand Down
43 changes: 37 additions & 6 deletions examples/gno.land/r/nt/boards2/v1/post.gno
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,13 @@ func (post *Post) GetURL() string {
}

func (post *Post) GetReplyFormURL() string {
if post.IsThread() {
return txlink.Call("CreateReply",
"boardID", post.board.id.String(),
"threadID", post.threadID.String(),
"replyID", "0",
)
}
return txlink.Call("CreateReply",
"boardID", post.board.id.String(),
"threadID", post.threadID.String(),
Expand Down Expand Up @@ -241,6 +248,24 @@ func (post *Post) GetDeleteFormURL() string {
)
}

func (post *Post) GetEditFormURL() string {
if post.IsThread() {
return txlink.Call("EditThread",
"boardID", post.board.id.String(),
"threadID", post.threadID.String(),
"title", post.GetTitle(),
"body", post.GetBody(),
)
}

return txlink.Call("EditReply",
"boardID", post.board.id.String(),
"threadID", post.threadID.String(),
"replyID", post.id.String(),
"body", post.GetBody(),
)
}

func (post *Post) GetFlagFormURL() string {
if post.IsThread() {
return txlink.Call("FlagThread",
Expand Down Expand Up @@ -325,21 +350,24 @@ func (post *Post) renderPostContent(sb *strings.Builder, indent string) {
sb.WriteString("\n")
}

postURL := post.GetURL()

// Buttons & counters
sb.WriteString(indent)
sb.WriteString(`\- `)
if !post.IsThread() {
sb.WriteString(" \n" + indent)
}

sb.WriteString(newUserLink(post.creator))
sb.WriteString(", ")
sb.WriteString(newLink(post.createdAt.Format(dateFormat), postURL))
sb.WriteString(post.createdAt.Format(dateFormat))

if post.repostsCount > 0 {
sb.WriteString(", ")
sb.WriteString(strconv.FormatUint(post.repostsCount, 10))
sb.WriteString(" reposts")
}

sb.WriteString(" - ")

if srcPost != nil {
sb.WriteString(" ")
sb.WriteString(newButtonLink("see source post", srcPost.GetURL()))
Expand All @@ -353,6 +381,9 @@ func (post *Post) renderPostContent(sb *strings.Builder, indent string) {
sb.WriteString(newButtonLink("repost", post.GetRepostFormURL()))
}

sb.WriteString(" ")
sb.WriteString(newButtonLink("edit", post.GetEditFormURL()))

sb.WriteString(" ")
sb.WriteString(newButtonLink("flag", post.GetFlagFormURL()))

Expand All @@ -367,7 +398,7 @@ func (post *Post) Render(p *PaginationOpts, indent string, levels int) string {
}

// TODO: pass a builder as arg into Render.
sb := &strings.Builder{}
var sb strings.Builder

if post.title != "" {
sb.WriteString(indent)
Expand All @@ -378,7 +409,7 @@ func (post *Post) Render(p *PaginationOpts, indent string, levels int) string {
sb.WriteString("\n")
}

post.renderPostContent(sb, indent)
post.renderPostContent(&sb, indent)

if post.replies.Size() == 0 {
return sb.String()
Expand Down
9 changes: 8 additions & 1 deletion examples/gno.land/r/nt/boards2/v1/post_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,16 @@ func TestNewThread(t *testing.T) {
uint(threadID),
)
replyURL := ufmt.Sprintf(
"/r/nt/boards2/v1$help&func=CreateReply&boardID=%d&replyID=%d&threadID=%d",
"/r/nt/boards2/v1$help&func=CreateReply&boardID=%d&replyID=0&threadID=%d",
uint(boardID),
uint(threadID),
)
editURL := ufmt.Sprintf(
"/r/nt/boards2/v1$help&func=EditThread&boardID=%d&body=%s&threadID=%d&title=%s",
uint(boardID),
body,
uint(threadID),
strings.ReplaceAll(title, " ", "+"),
)
repostURL := ufmt.Sprintf(
"/r/nt/boards2/v1$help&func=CreateRepost&boardID=%d&threadID=%d",
Expand Down Expand Up @@ -151,6 +157,7 @@ func TestNewThread(t *testing.T) {
uassert.False(t, thread.HasReplies())
uassert.Equal(t, url, thread.GetURL())
uassert.Equal(t, replyURL, thread.GetReplyFormURL())
uassert.Equal(t, editURL, thread.GetEditFormURL())
uassert.Equal(t, repostURL, thread.GetRepostFormURL())
uassert.Equal(t, deleteURL, thread.GetDeleteFormURL())
uassert.Equal(t, flagURL, thread.GetFlagFormURL())
Expand Down
81 changes: 81 additions & 0 deletions examples/gno.land/r/nt/boards2/v1/render.gno
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package boards2

import (
"net/url"
"strconv"
"strings"

"gno.land/p/demo/mux"
"gno.land/p/moul/txlink"
)

const (
Expand All @@ -12,6 +15,11 @@ const (
repliesPageSize = 30
)

const (
menuAdmin = "admin"
menuMembership = "membership"
)

func Render(path string) string {
router := mux.NewRouter()
router.HandleFunc("", renderBoardsList)
Expand All @@ -27,6 +35,8 @@ func Render(path string) string {
}

func renderBoardsList(res *mux.ResponseWriter, req *mux.Request) {
renderBoardListMenu(res, req)

res.Write("These are all the boards of this realm:\n\n")
p := mustGetPagination(req.RawPath, boardsPageSize)
page := p.Iterate(&gBoardsByID, func(_ string, value interface{}) bool {
Expand All @@ -43,6 +53,26 @@ func renderBoardsList(res *mux.ResponseWriter, req *mux.Request) {
}
}

func renderBoardListMenu(res *mux.ResponseWriter, req *mux.Request) {
res.Write(newButtonLink("create board", txlink.Call("CreateBoard")) + " - ")

menu := getCurrentSubmenu(req.RawPath)
if menu == menuMembership {
res.Write("**membership**")
} else {
res.Write(newButtonLink("membership", submenuURL(menuMembership)))
}

res.Write("\n\n")

if menu == menuMembership {
res.Write("↳")
res.Write(newButtonLink("invite", txlink.Call("InviteMember", "boardID", "0")) + " ")
res.Write(newButtonLink("remove member", txlink.Call("RemoveMember", "boardID", "0")) + " ")
res.Write(newButtonLink("change member role", txlink.Call("ChangeMemberRole", "boardID", "0")) + "\n\n")
}
}

func renderBoard(res *mux.ResponseWriter, req *mux.Request) {
name := req.GetVar("board")
v, found := gBoardsByName.Get(name)
Expand All @@ -52,10 +82,46 @@ func renderBoard(res *mux.ResponseWriter, req *mux.Request) {
}

board := v.(*Board)
renderBoardMenu(board, res, req)

p := mustGetPagination(req.RawPath, threadsPageSize)
res.Write(board.Render(p))
}

func renderBoardMenu(board *Board, res *mux.ResponseWriter, req *mux.Request) {
res.Write(newButtonLink("post", board.GetPostFormURL()) + " - ")

menu := getCurrentSubmenu(req.RawPath)
if menu == menuMembership {
res.Write("**membership** - ")
} else {
res.Write(newButtonLink("membership", submenuURL(menuMembership)) + " - ")
}

if menu == menuAdmin {
res.Write("**admin**")
} else {
res.Write(newButtonLink("admin", submenuURL(menuAdmin)))
}

res.Write("\n\n")

if menu != "" {
res.Write("↳")
}

switch menu {
case menuAdmin:
res.Write(newButtonLink("rename board", board.GetRenameFormURL()) + " ")
res.Write(newButtonLink("freeze", board.GetFreezeFormURL()) + " ")
res.Write(newButtonLink("change flagging threshold", board.GetFlaggingThresholdFormURL()) + "\n\n")
case menuMembership:
res.Write(newButtonLink("invite", board.GetInviteMemberFormURL()) + " ")
res.Write(newButtonLink("remove member", board.GetRemoveMemberFormURL()) + " ")
res.Write(newButtonLink("change member role", board.GetChangeMemberRoleFormURL()) + "\n\n")
}
}

func renderThread(res *mux.ResponseWriter, req *mux.Request) {
name := req.GetVar("board")
v, found := gBoardsByName.Get(name)
Expand Down Expand Up @@ -123,3 +189,18 @@ func renderReply(res *mux.ResponseWriter, req *mux.Request) {
// See: #3480
res.Write(reply.RenderInner())
}

func submenuURL(name string) string {
// TODO: Submenu URL works because no other GET arguments are being used
return "?submenu=" + name
}

func getCurrentSubmenu(rawURL string) string {
_, rawQuery, found := strings.Cut(rawURL, "?")
if !found {
return ""
}

query, _ := url.ParseQuery(rawQuery)
return query.Get("submenu")
}

0 comments on commit 47347a5

Please sign in to comment.