Skip to content
Open
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
da88b1d
wip: refactoring board types and figuring out patterns
jeronimoalbi Sep 29, 2025
9c980b4
Merge branch 'master' into feat/boards-package
jeronimoalbi Sep 30, 2025
d5335ff
chore: fix source to work with `stdlib` refactor
jeronimoalbi Sep 30, 2025
46879e5
tests: add filetests for reply post type
jeronimoalbi Oct 1, 2025
694aab0
Merge branch 'master' into feat/boards-package
jeronimoalbi Oct 1, 2025
c4ab81f
chore: fix reply filetests to work with latest stdlib changes
jeronimoalbi Oct 1, 2025
e29d4fc
tests: add filetests for thread post type
jeronimoalbi Oct 2, 2025
e33acd9
chore: remove Post.UpdatedAt public property
jeronimoalbi Oct 2, 2025
ef5b29a
wip: refactor types to focus on public properties instead of set/getters
jeronimoalbi Oct 2, 2025
e063860
wip: refactor post replies into a post storage
jeronimoalbi Oct 2, 2025
13f5053
wip: refactor post flags into a storage
jeronimoalbi Oct 2, 2025
9006f71
wip: refactor reposts into a storage
jeronimoalbi Oct 2, 2025
ce3f57b
tests: remove filetest to favor unit tests
jeronimoalbi Oct 2, 2025
4e05409
chore: address pending TODOs
jeronimoalbi Oct 2, 2025
3e5cb04
tests: add unit test for identifier generator
jeronimoalbi Oct 2, 2025
6216e37
tests: add unit tests for flag storage
jeronimoalbi Oct 2, 2025
c8f7c77
tests: add unit tests for post storage
jeronimoalbi Oct 2, 2025
25a4cbb
tests: add unit tests for repost storage
jeronimoalbi Oct 3, 2025
f8e87b2
tests: add unit tests for boards storage
jeronimoalbi Oct 3, 2025
6c6915c
tests: add unit test for boards
jeronimoalbi Oct 3, 2025
41767db
tests: add unit test for replies
jeronimoalbi Oct 3, 2025
58f52d7
tests: add unit tests for thread
jeronimoalbi Oct 3, 2025
417ccec
tests: add unit tests for post realated features
jeronimoalbi Oct 6, 2025
29f9108
chore: remove `Post.DeleteReply()` and its unit test
jeronimoalbi Oct 6, 2025
ce11b30
chore: remove `Post.Save()`
jeronimoalbi Oct 6, 2025
562a673
fix: change `IsThread()` to check that thread is not a repost
jeronimoalbi Oct 6, 2025
3ee7967
Merge branch 'master' into feat/boards-package
jeronimoalbi Oct 6, 2025
2ff5375
chore: fix `IsThread()` to consider reposts also as threads
jeronimoalbi Oct 8, 2025
076a3ff
Merge branch 'master' into feat/boards-package
jeronimoalbi Oct 8, 2025
46adb95
Merge branch 'master' into feat/boards-package
jeronimoalbi Oct 10, 2025
514d106
Merge branch 'master' into feat/boards-package
jeronimoalbi Oct 14, 2025
97f6f20
Merge branch 'master' into feat/boards-package
jeronimoalbi Oct 15, 2025
0613b20
Merge branch 'master' into feat/boards-package
jeronimoalbi Oct 16, 2025
8c5d37d
Merge branch 'master' into feat/boards-package
jeronimoalbi Oct 20, 2025
731300a
Merge branch 'master' into feat/boards-package
jeronimoalbi Oct 22, 2025
029eba5
feat: add post and board setters and meta support
jeronimoalbi Oct 27, 2025
85eb535
Merge branch 'master' into feat/boards-package
jeronimoalbi Nov 5, 2025
995780c
Merge branch 'master' into feat/boards-package
jeronimoalbi Nov 11, 2025
fe80f3d
Merge branch 'master' into feat/boards-package
jeronimoalbi Nov 13, 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
48 changes: 48 additions & 0 deletions examples/gno.land/p/gnoland/boards/board.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package boards

import "time"

// Board defines a type for boards.
type Board struct {
// ID is the unique identifier of the board.
ID ID

// Name is the current name of the board.
Name string

// Aliases contains a list of alternative names for the board.
Aliases []string

// Readonly indicates that the board is readonly.
Readonly bool

// Threads contains all board threads.
Threads PostStorage

// ThreadsSequence generates sequential ID for new threads.
ThreadsSequence IdentifierGenerator

// Permissions enables support for permissioned boards.
// This type of boards allows managing members with roles and permissions.
// It also enables the implementation of permissioned execution of board related features.
Permissions Permissions

// Creator is the account address that created the board.
Creator address

// CreatedAt is the board's creation time.
CreatedAt time.Time

// UpdatedAt is the board's update time.
UpdatedAt time.Time
}

// New creates a new basic non permissioned board.
func New(id ID) *Board {
return &Board{
ID: id,
Threads: NewPostStorage(),
ThreadsSequence: NewIdentifierGenerator(),
CreatedAt: time.Now(),
}
}
18 changes: 18 additions & 0 deletions examples/gno.land/p/gnoland/boards/board_test.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package boards_test

import (
"testing"

"gno.land/p/nt/urequire"

"gno.land/p/gnoland/boards"
)

func TestNew(t *testing.T) {
board := boards.New(42)

urequire.Equal(t, 42, int(board.ID), "expect board ID to match")
urequire.True(t, board.Threads != nil, "expect board to support threads")
urequire.True(t, board.ThreadsSequence != nil, "expect board to initialize a thread ID generator")
urequire.False(t, board.CreatedAt.IsZero(), "expect board to have a creation date")
}
105 changes: 105 additions & 0 deletions examples/gno.land/p/gnoland/boards/flag_storage.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package boards

import (
"strings"

"gno.land/p/nt/avl"
"gno.land/p/nt/ufmt"
)

type (
// Flag defines a type for post flags
Flag struct {
// User is the user that flagged the post.
User address

// Reason is the reason that describes why post is flagged.
Reason string
}

// FlagIterFn defines a function type to iterate post flags.
FlagIterFn func(Flag) bool

// FlagStorage defines an interface for storing posts flagging information.
FlagStorage interface {
// Exists checks if a flag from a user exists
Exists(address) bool

// Add adds a new flag from a user.
Add(Flag) error

// Remove removes a user flag.
Remove(address) (removed bool)

// Size returns the number of flags in the storage.
Size() int

// Iterate iterates post flags.
// To reverse iterate flags use a negative count.
// If the callback returns true, the iteration is stopped.
Iterate(start, count int, fn FlagIterFn) bool
}
)

// NewFlagStorage creates a new storage for post flags.
// The new storage uses an AVL tree to store flagging info.
func NewFlagStorage() FlagStorage {
return &flagStorage{avl.NewTree()}
}

type flagStorage struct {
flags *avl.Tree // address -> string(reason)
}

// Exists checks if a flag from a user exists
func (s flagStorage) Exists(addr address) bool {
return s.flags.Has(addr.String())
}

// Add adds a new flag from a user.
// It fails if a flag from the same user exists.
func (s *flagStorage) Add(f Flag) error {
if !f.User.IsValid() {
return ufmt.Errorf("post flagging error, invalid user address: %s", f.User)
}

k := f.User.String()
if s.flags.Has(k) {
return ufmt.Errorf("flag from user already exists: %s", f.User)
}

s.flags.Set(k, strings.TrimSpace(f.Reason))
return nil
}

// Remove removes a user flag.
func (s *flagStorage) Remove(addr address) bool {
_, removed := s.flags.Remove(addr.String())
return removed
}

// Size returns the number of flags in the storage.
func (s flagStorage) Size() int {
return s.flags.Size()
}

// Iterate iterates post flags.
// To reverse iterate flags use a negative count.
// If the callback returns true, the iteration is stopped.
func (s flagStorage) Iterate(start, count int, fn FlagIterFn) bool {
if count < 0 {
return s.flags.ReverseIterateByOffset(start, -count, func(k string, v any) bool {
return fn(Flag{
User: address(k),
Reason: v.(string),
})
})
}

return s.flags.IterateByOffset(start, count, func(k string, v any) bool {
return fn(Flag{
User: address(k),
Reason: v.(string),
})
})
}
Loading
Loading