-
Notifications
You must be signed in to change notification settings - Fork 375
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Manfred Touron <[email protected]> Co-authored-by: Jae Kwon <[email protected]> Co-authored-by: Jeff Thompson <[email protected]> Co-authored-by: Hariom Verma <[email protected]> Co-authored-by: Morgan <[email protected]> Co-authored-by: Albert Le Batteux <[email protected]> Co-authored-by: Guilhem Fanton <[email protected]> Co-authored-by: Blake <[email protected]> Co-authored-by: Jeff Thompson <[email protected]> Co-authored-by: Leon Hudak <[email protected]> Co-authored-by: Poroburu <[email protected]> Co-authored-by: deelawn <[email protected]> Co-authored-by: grepsuzette <[email protected]> Co-authored-by: jon roethke <[email protected]>
- Loading branch information
1 parent
42d5089
commit 1510a6f
Showing
8 changed files
with
802 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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}$`) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
Oops, something went wrong.