Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(boards2): implement next boards version #2901

Open
wants to merge 117 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
117 commits
Select commit Hold shift + click to select a range
71c93a7
feat(boardsv2): init (#2900)
ilgooz Oct 4, 2024
baf9821
Merge branch 'master' into devx/feature/boardsv2
ilgooz Oct 4, 2024
1a88d58
feat(boardsv2): experiment API - WIP (#2902)
jeronimoalbi Nov 4, 2024
7408299
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Nov 11, 2024
42d5089
chore: remove `boardsv2` drafts (#3111)
jeronimoalbi Nov 12, 2024
1510a6f
chore: copy `boards` realm to `boards2` (#3110)
jeronimoalbi Nov 12, 2024
4c7d16b
feat: simplify `boards2` implementation (#3115)
jeronimoalbi Nov 13, 2024
8874e8e
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Nov 15, 2024
dd21273
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Nov 19, 2024
1f6d61f
test(boards2): unit tests for `Board` and `Post` types (#3150)
jeronimoalbi Nov 20, 2024
7446197
feat(boards2): update package name to "boards2" (#3184)
x1unix Nov 22, 2024
c31350c
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Nov 25, 2024
4944a7e
feat(boards2): initial permissions support (#3151)
jeronimoalbi Nov 26, 2024
4027d07
feat(boards2): board creation and permissions (#3211)
jeronimoalbi Nov 29, 2024
20fc146
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Dec 18, 2024
75cd4ab
fix: resolve devx boards2 branch issues (#3365)
jeronimoalbi Dec 18, 2024
251a107
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Dec 18, 2024
261e316
feat: permissions support for individual boards (#3386)
jeronimoalbi Dec 23, 2024
0e7d24f
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Dec 23, 2024
b8ac6b0
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Jan 6, 2025
8844f30
feat(boards2): add core flagging logic (#3451)
x1unix Jan 7, 2025
efbfd00
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Jan 7, 2025
3c41887
chore(boards2): remove permission handlers (#3449)
jeronimoalbi Jan 7, 2025
25622db
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Jan 7, 2025
40eead0
feat(boards2): board renaming (#3462)
jeronimoalbi Jan 9, 2025
39e19d3
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Jan 9, 2025
0556dc8
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Jan 15, 2025
4090aac
feat(boardsv2): initialize reposting (#3513)
x1unix Jan 16, 2025
541278b
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Jan 16, 2025
adaafd7
feat(boards2): member role management (#3512)
jeronimoalbi Jan 16, 2025
e60574f
feat(boardsv2): hide flagged comments (#3536)
x1unix Jan 17, 2025
a10d2c7
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Jan 17, 2025
1e24a33
feat(boards2): initial commondao implementation (#3558)
jeronimoalbi Jan 22, 2025
2f99874
feat(boards2): change public delete reply function to soft delete rep…
jeronimoalbi Jan 27, 2025
dc5a753
feat(boards2): improve public realm functions (#3583)
jeronimoalbi Jan 27, 2025
e769fb1
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Jan 27, 2025
48a8042
feat(boards2): pagination (#3586)
x1unix Jan 27, 2025
6592035
feat(boards2): add proposal definition tally support to `commondao` p…
jeronimoalbi Jan 28, 2025
5f3a9f6
chore(boards2): move `commondao` & `boards2` to "nt" namespace (#3618)
jeronimoalbi Jan 28, 2025
69960ec
test(boards2): add missing filetests for board creation (#3622)
jeronimoalbi Jan 28, 2025
33fb889
test(boards2): add missing filetests for board rename (#3625)
jeronimoalbi Jan 28, 2025
90a3c4e
test(boards2): add missing filetests for member invite (#3632)
jeronimoalbi Jan 30, 2025
dc173c2
test(boards2): add missing filetests for member removal (#3633)
jeronimoalbi Jan 30, 2025
63007ab
test(boards2): add missing filetests for member role change (#3636)
jeronimoalbi Jan 30, 2025
bab1153
test(boards2): add missing filetests for member check (#3638)
jeronimoalbi Jan 30, 2025
b00f9e1
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Jan 30, 2025
db1dd43
test(boards2): add missing filetests for thread creation (#3644)
jeronimoalbi Jan 30, 2025
33da5ed
test(boards2): add missing filetests for thread edit (#3649)
jeronimoalbi Jan 31, 2025
d7dcd92
test(boards2): add missing filetests for thread flagging (#3646)
jeronimoalbi Jan 31, 2025
d7740a4
test(boards2): add missing filetests for thread deletion (#3645)
jeronimoalbi Jan 31, 2025
5f67719
test(boards2): add missing filetests for board ID getter by name (#3642)
jeronimoalbi Jan 31, 2025
60334e6
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Jan 31, 2025
b983f8f
test(boards2): add missing filetests for reply creation (#3653)
jeronimoalbi Jan 31, 2025
2203553
test(boards2): add missing filetests for reply edit (#3655)
jeronimoalbi Feb 3, 2025
277b49d
test(boards2): add missing filetests for reply flagging (#3656)
jeronimoalbi Feb 3, 2025
d2b4892
test(boards2): add missing filetests for reply deletion (#3657)
jeronimoalbi Feb 3, 2025
edc5d69
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Feb 3, 2025
a7c56e2
feat(boards2): repost tests (#3659)
x1unix Feb 4, 2025
7a94b18
chore(boards2): move all files into `v1` folder (#3680)
jeronimoalbi Feb 4, 2025
87f9089
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Feb 6, 2025
a8c80b7
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Feb 10, 2025
a9c821a
chore: fix issues after latest `master` merge
jeronimoalbi Feb 10, 2025
c948518
feat(boards2): change flagging threshold to be configurable per board…
jeronimoalbi Feb 13, 2025
828a084
chore(boards2): remove legacy user call asserts (#3756)
jeronimoalbi Feb 15, 2025
98002bb
chore(boards2): add initial documentation to public realm functions (…
jeronimoalbi Feb 15, 2025
c1e5959
chore(boards2): simplify boards realm permissions (#3717)
jeronimoalbi Feb 15, 2025
fed38e6
feat(boards2): add support to freeze individual boards (#3755)
jeronimoalbi Feb 17, 2025
871d13e
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Feb 17, 2025
72c1407
chore(boards2): simplify default permissions implementation (#3770)
jeronimoalbi Feb 18, 2025
17377ea
chore: remove `GetDAO` from `Permissions` interface
jeronimoalbi Feb 20, 2025
e88eafa
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Feb 20, 2025
df84cbe
fix(boards2): change `boards2` realm to work with breaking `master` c…
jeronimoalbi Feb 21, 2025
4009304
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Feb 21, 2025
c80653b
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Feb 23, 2025
47347a5
feat(boards2): improve menus and add missing actions (#3809)
jeronimoalbi Feb 24, 2025
d4ee2c9
feat(boards2): allow changing permissions implementation (#3718)
jeronimoalbi Feb 24, 2025
edf5629
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Feb 25, 2025
129cccb
feat(boards2): add views to list board members (#3803)
jeronimoalbi Feb 25, 2025
ddafaf1
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Feb 26, 2025
c5b1a71
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Feb 27, 2025
e7539f3
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Mar 3, 2025
9891d6b
feat(boards2): change flagging to allow realm owners to hide with a s…
jeronimoalbi Mar 6, 2025
c3aaac2
feat(boards2): implement freeze and unfreeze for thread and reply (#3…
x1unix Mar 6, 2025
72d2d4a
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Mar 10, 2025
2b2ee02
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Mar 11, 2025
8d3610a
fix(boards2): change realm to work with new users realm (#3916)
jeronimoalbi Mar 11, 2025
2a0b6d4
feat(boards2): add events to boards2 realm (#3915)
jeronimoalbi Mar 14, 2025
29ed82c
fix(boards2): correct usability and UI issues (#3848)
jeronimoalbi Mar 14, 2025
137a121
feat(boards2): change pager dependency to be consistent (#3953)
jeronimoalbi Mar 16, 2025
4b427cc
feat(boards2): check replies length and disallow UI breaking Markdown…
jeronimoalbi Mar 17, 2025
9c57c0b
chore: deprecate `OriginCaller` in favor of `PreviousRealm`
jeronimoalbi Mar 17, 2025
6c25eb4
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Mar 17, 2025
f190d53
chore: fix `boards2` filetest to work with new `users` realm constraints
jeronimoalbi Mar 17, 2025
b715919
feat(boards2): simplify freeze implementation (#3960)
jeronimoalbi Mar 17, 2025
bc9cb0b
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Mar 17, 2025
810c71b
feat(boards2): add realm and realm members locking support (#3969)
jeronimoalbi Mar 19, 2025
08b9225
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Mar 19, 2025
b05a282
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Mar 19, 2025
8d606b5
feat(boards2): add public iteration functions to read minimal board d…
jeronimoalbi Mar 19, 2025
5fd2e4b
chore: move `r/nt/boards2` to `r/gnoland/boards2`
jeronimoalbi Mar 19, 2025
9e80f73
chore: remove `commondao` package from `devx/feature/boardsv2`
jeronimoalbi Mar 19, 2025
625e353
chore: change `boards2` to use `gno.land/p/gnoland/commondao` package
jeronimoalbi Mar 19, 2025
0079189
chore: fix imports sorting
jeronimoalbi Mar 19, 2025
d46ce08
fix: correct default permissions impl to work with commondao
jeronimoalbi Mar 20, 2025
c65bb87
feat: use a markdown table to list members
jeronimoalbi Mar 20, 2025
4aba74e
chore: address some UI review related improvements
jeronimoalbi Mar 20, 2025
5e99c99
feat: add boards dApp help content support
jeronimoalbi Mar 20, 2025
cd6b64a
chore: move board menu under the board section title
jeronimoalbi Mar 20, 2025
2f482e2
feat: improve thread and reply actions layout
jeronimoalbi Mar 20, 2025
4467569
feat: support reply views
jeronimoalbi Mar 20, 2025
26b814c
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Mar 20, 2025
352c845
chore: remove separatos when rendering reply action links
jeronimoalbi Mar 21, 2025
6221e90
chore: improve board not found message txlink
jeronimoalbi Mar 21, 2025
0eafc14
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Mar 26, 2025
07929e9
test: fix tests for the latest gno API changes
jeronimoalbi Mar 26, 2025
f1d2c33
feat(boards2): add boards, thread and reply sorting support by oldest…
jeronimoalbi Mar 26, 2025
ef9a073
Merge branch 'master' into devx/feature/boardsv2
jeronimoalbi Mar 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
260 changes: 260 additions & 0 deletions examples/gno.land/r/gnoland/boards2/v1/board.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
package boards2

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

"gno.land/p/demo/avl"
"gno.land/p/gnoland/commondao"
"gno.land/p/jeronimoalbi/pager"
"gno.land/p/moul/realmpath"
"gno.land/p/moul/txlink"
)

type BoardID uint64

func (id BoardID) String() string {
return strconv.Itoa(int(id))
}

func (id BoardID) Key() string {
return padZero(uint64(id), 10)
}

type Board struct {
id BoardID // only set for public boards.
name string
aliases []string
creator std.Address
threads avl.Tree // Post.id -> *Post
postsCtr uint64 // increments Post.id
createdAt time.Time
deleted avl.Tree // TODO reserved for fast-delete.
perms Permissions
readonly bool
}

func newBoard(id BoardID, name string, creator std.Address, p Permissions) *Board {
return &Board{
id: id,
name: name,
creator: creator,
threads: avl.Tree{},
createdAt: time.Now(),
deleted: avl.Tree{},
perms: p,
}
}

func (board *Board) GetID() BoardID {
return board.id
}

// GetName returns the name of the board.
func (board *Board) GetName() string {
return board.name
}

// GetURL returns the relative URL of the board.
func (board *Board) GetURL() string {
return strings.TrimPrefix(std.CurrentRealm().PkgPath(), "gno.land") + ":" + url.PathEscape(board.name)
}

// GetURL returns relative board path relative.
//
// Note: returned result is not escaped. Use GetURL to get URL-encoded path.
func (board *Board) GetPath() string {
return strings.TrimPrefix(std.CurrentRealm().PkgPath(), "gno.land") + ":" + board.name
}

func (board *Board) GetPermissions() Permissions {
return board.perms
}

// SetReadonly updates board's read-only status.
func (board *Board) SetReadonly(readonly bool) {
board.readonly = readonly
}

// IsReadonly checks if the board is a read-only board.
func (board *Board) IsReadonly() bool {
return board.readonly
}

func (board *Board) GetThread(threadID PostID) (_ *Post, found bool) {
v, found := board.threads.Get(threadID.Key())
if !found {
return nil, false
}
return v.(*Post), true
}

func (board *Board) AddThread(creator std.Address, title string, body string) *Post {
pid := board.incGetPostID()
thread := newPost(board, pid, creator, title, body, pid, 0, 0)
board.threads.Set(pid.Key(), thread)
return thread
}

// NOTE: this can be potentially very expensive for threads with many replies.
// TODO: implement optional fast-delete where thread is simply moved.
func (board *Board) DeleteThread(pid PostID) {
_, removed := board.threads.Remove(pid.Key())
if !removed {
panic("thread does not exist with ID " + pid.String())
}
}

// Render renders a board into Markdown.
func (board *Board) Render(path, menu string) string {
var sb strings.Builder

sb.WriteString("# " + board.GetName() + " \n")
sb.WriteString("_Board created by " + newUserLink(board.creator) + " ")
sb.WriteString("on " + board.createdAt.Format(dateFormat))
sb.WriteString(", #" + board.id.String() + "_")

if board.IsReadonly() {
sb.WriteString(" \n_**Starting new threads and commenting is disabled**_")
}

// XXX: Menu is rendered by the caller to deal with links and sub-menus
// TODO: We should have the render logic separated from boards so avoid sending menu as argument
if menu != "" {
sb.WriteString("\n\n")
sb.WriteString(menu)
sb.WriteString("\n")
}

sb.WriteString("\n\n---\n")

if board.threads.Size() == 0 {
sb.WriteString("### This board doesn't have any threads\n")
if !board.IsReadonly() {
url := board.GetPostFormURL()
sb.WriteString("Do you want to [start a new conversation](" + url + ") in this board ?")
}
return sb.String()
}

p, err := pager.New(path, board.threads.Size(), pager.WithPageSize(pageSizeThreads))
if err != nil {
panic(err)
}

render := func(_ string, v any) bool {
thread := v.(*Post)
if thread.isHidden {
return false
}

sb.WriteString(thread.RenderSummary())
sb.WriteString("\n")
return false
}

sb.WriteString("_Sort by ")
r := realmpath.Parse(path)
if r.Query.Get("order") == "desc" {
r.Query.Set("order", "asc")
sb.WriteString(newLink("newest first", r.String()))
sb.WriteString("_\n")
board.threads.ReverseIterateByOffset(p.Offset(), p.PageSize(), render)
} else {
r.Query.Set("order", "desc")
sb.WriteString(newLink("oldest first", r.String()))
sb.WriteString("_\n")
board.threads.IterateByOffset(p.Offset(), p.PageSize(), render)
}

if p.HasPages() {
sb.WriteString("\n---\n")
sb.WriteString(pager.Picker(p))
}

return sb.String()
}

func (board *Board) incGetPostID() PostID {
board.postsCtr++
return PostID(board.postsCtr)
}

func (board *Board) GetURLFromThreadID(threadID PostID) string {
return board.GetURL() + "/" + threadID.String()
}

func (board *Board) GetURLFromReplyID(threadID, replyID PostID) string {
return board.GetURL() + "/" + threadID.String() + "/" + replyID.String()
}

func (board *Board) GetRenameFormURL() string { // TODO: These type of functions shouldn't be part of Board type
return txlink.Call("RenameBoard", "name", board.name)
}

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

func (board *Board) GetUnfreezeFormURL() string {
return txlink.Call("UnfreezeBoard", "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) GetPostFormURL() string {
return txlink.Call("CreateThread", "boardID", board.id.String())
}

func (board *Board) GetMembersURL() string {
return board.GetURL() + "/members"
}

func createDefaultBoardPermissions(owner std.Address) *DefaultPermissions {
perms := NewDefaultPermissions(commondao.New())
perms.SetSuperRole(RoleOwner)
perms.AddRole(
RoleAdmin,
PermissionBoardRename,
PermissionBoardFlaggingUpdate,
PermissionMemberInvite,
PermissionMemberRemove,
PermissionThreadCreate,
PermissionThreadEdit,
PermissionThreadDelete,
PermissionThreadRepost,
PermissionThreadFlag,
PermissionThreadFreeze,
PermissionReplyCreate,
PermissionReplyDelete,
PermissionReplyFlag,
PermissionReplyFreeze,
PermissionRoleChange,
)
perms.AddRole(
RoleModerator,
PermissionThreadCreate,
PermissionThreadEdit,
PermissionThreadRepost,
PermissionThreadFlag,
PermissionReplyCreate,
PermissionReplyFlag,
)
perms.AddRole(
RoleGuest,
PermissionThreadCreate,
PermissionThreadRepost,
PermissionReplyCreate,
)
perms.AddUser(owner, RoleOwner)
return perms
}
80 changes: 80 additions & 0 deletions examples/gno.land/r/gnoland/boards2/v1/board_readonly.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package boards2

import (
"std"
"time"
)

// ReadonlyBoard defines an interface for readonly boards.
type ReadonlyBoard interface {
// ID returns the unique identifier of the board.
ID() BoardID

// Name returns the name of the board.
Name() string

// Aliases returns a list with previous names assigned to the board.
Aliases() []string

// Creator returns board's creator address.
Creator() std.Address

// LastPostID returns the ID assigned to the last created thread.
LastPostID() uint64

// CreatedAt returns the time when board was created.
CreatedAt() time.Time

// IsReadonly returns true when board shouldn't allow the creation of threads and comments.
IsReadonly() bool

// MembersCount returns the number of invited board members.
MembersCount() int

// IterateMembers allows reading the list of invited board members.
IterateMembers(offset, count int, fn UsersIterFn)
}

type readonlyBoard struct {
board *Board
}

func (b readonlyBoard) ID() BoardID {
return b.board.id
}

func (b readonlyBoard) Name() string {
return b.board.name
}

func (b readonlyBoard) Aliases() []string {
aliases := make([]string, len(b.board.aliases))
for i, s := range b.board.aliases {
aliases[i] = s
}
return aliases
}

func (b readonlyBoard) Creator() std.Address {
return b.board.creator
}

func (b readonlyBoard) LastPostID() uint64 {
return b.board.postsCtr
}

func (b readonlyBoard) CreatedAt() time.Time {
return b.board.createdAt
}

func (b readonlyBoard) IsReadonly() bool {
return b.board.readonly
}

func (b readonlyBoard) MembersCount() int {
return b.board.perms.UsersCount()
}

func (b readonlyBoard) IterateMembers(offset, count int, fn UsersIterFn) {
b.board.perms.IterateUsers(offset, count, fn)
}
Loading
Loading