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

chore: "copy" boards into boards2 #3

Merged
Merged
Changes from all commits
Commits
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
139 changes: 139 additions & 0 deletions examples/gno.land/r/demo/boards2/board.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package boards

import (
"std"
"strconv"
"time"

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

//----------------------------------------
// Board

type BoardID uint64

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

type Board struct {
id BoardID // only set for public boards.
url string
name 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.
}

func newBoard(id BoardID, url string, name string, creator std.Address) *Board {
if !reName.MatchString(name) {
panic("invalid name: " + name)
}
exists := gBoardsByName.Has(name)
if exists {
panic("board already exists")
}
return &Board{
id: id,
url: url,
name: name,
creator: creator,
threads: avl.Tree{},
createdAt: time.Now(),
deleted: avl.Tree{},
}
}

/* TODO support this once we figure out how to ensure URL correctness.
// A private board is not tracked by gBoards*,
// but must be persisted by the caller's realm.
// Private boards have 0 id and does not ping
// back the remote board on reposts.
func NewPrivateBoard(url string, name string, creator std.Address) *Board {
return newBoard(0, url, name, creator)
}
*/

func (board *Board) IsPrivate() bool {
return board.id == 0
}

func (board *Board) GetThread(pid PostID) *Post {
pidkey := postIDKey(pid)
postI, exists := board.threads.Get(pidkey)
if !exists {
return nil
}
return postI.(*Post)
}

func (board *Board) AddThread(creator std.Address, title string, body string) *Post {
pid := board.incGetPostID()
pidkey := postIDKey(pid)
thread := newPost(board, pid, creator, title, body, pid, 0, 0)
board.threads.Set(pidkey, 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) {
pidkey := postIDKey(pid)
_, removed := board.threads.Remove(pidkey)
if !removed {
panic("thread does not exist with id " + pid.String())
}
}

func (board *Board) HasPermission(addr std.Address, perm Permission) bool {
if board.creator == addr {
switch perm {
case EditPermission:
return true
case DeletePermission:
return true
default:
return false
}
}
return false
}

// Renders the board for display suitable as plaintext in
// console. This is suitable for demonstration or tests,
// but not for prod.
func (board *Board) RenderBoard() string {
str := ""
str += "\\[[post](" + board.GetPostFormURL() + ")]\n\n"
if board.threads.Size() > 0 {
board.threads.Iterate("", "", func(key string, value interface{}) bool {
if str != "" {
str += "----------------------------------------\n"
}
str += value.(*Post).RenderSummary() + "\n"
return false
})
}
return str
}

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

func (board *Board) GetURLFromThreadAndReplyID(threadID, replyID PostID) string {
if replyID == 0 {
return board.url + "/" + threadID.String()
} else {
return board.url + "/" + threadID.String() + "/" + replyID.String()
}
}

func (board *Board) GetPostFormURL() string {
return txlink.URL("CreateThread", "bid", board.id.String())
}
22 changes: 22 additions & 0 deletions examples/gno.land/r/demo/boards2/boards.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package boards

import (
"regexp"

"gno.land/p/demo/avl"
)

//----------------------------------------
// Realm (package) state

var (
gBoards avl.Tree // id -> *Board
gBoardsCtr int // increments Board.id
gBoardsByName avl.Tree // name -> *Board
gDefaultAnonFee = 100000000 // minimum fee required if anonymous
)

//----------------------------------------
// Constants

var reName = regexp.MustCompile(`^[a-z]+[_a-z0-9]{2,29}$`)
7 changes: 7 additions & 0 deletions examples/gno.land/r/demo/boards2/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module gno.land/r/demo/boards2

require (
gno.land/p/demo/avl v0.0.0-latest
gno.land/p/moul/txlink v0.0.0-latest
gno.land/r/demo/users v0.0.0-latest
)
95 changes: 95 additions & 0 deletions examples/gno.land/r/demo/boards2/misc.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package boards

import (
"std"
"strconv"
"strings"

"gno.land/r/demo/users"
)

//----------------------------------------
// private utility methods
// XXX ensure these cannot be called from public.

func getBoard(bid BoardID) *Board {
bidkey := boardIDKey(bid)
board_, exists := gBoards.Get(bidkey)
if !exists {
return nil
}
board := board_.(*Board)
return board
}

func incGetBoardID() BoardID {
gBoardsCtr++
return BoardID(gBoardsCtr)
}

func padLeft(str string, length int) string {
if len(str) >= length {
return str
} else {
return strings.Repeat(" ", length-len(str)) + str
}
}

func padZero(u64 uint64, length int) string {
str := strconv.Itoa(int(u64))
if len(str) >= length {
return str
} else {
return strings.Repeat("0", length-len(str)) + str
}
}

func boardIDKey(bid BoardID) string {
return padZero(uint64(bid), 10)
}

func postIDKey(pid PostID) string {
return padZero(uint64(pid), 10)
}

func indentBody(indent string, body string) string {
lines := strings.Split(body, "\n")
res := ""
for i, line := range lines {
if i > 0 {
res += "\n"
}
res += indent + line
}
return res
}

// NOTE: length must be greater than 3.
func summaryOf(str string, length int) string {
lines := strings.SplitN(str, "\n", 2)
line := lines[0]
if len(line) > length {
line = line[:(length-3)] + "..."
} else if len(lines) > 1 {
// len(line) <= 80
line = line + "..."
}
return line
}

func displayAddressMD(addr std.Address) string {
user := users.GetUserByAddress(addr)
if user == nil {
return "[" + addr.String() + "](/r/demo/users:" + addr.String() + ")"
} else {
return "[@" + user.Name + "](/r/demo/users:" + user.Name + ")"
}
}

func usernameOf(addr std.Address) string {
user := users.GetUserByAddress(addr)
if user == nil {
return ""
}
return user.Name
}
Loading