diff --git a/examples/gno.land/r/demo/boards2/flag.gno b/examples/gno.land/r/demo/boards2/flag.gno new file mode 100644 index 000000000000..46a9cdada2da --- /dev/null +++ b/examples/gno.land/r/demo/boards2/flag.gno @@ -0,0 +1,47 @@ +package boards2 + +import ( + "std" + "strconv" +) + +var flagThreshold = 3 + +type Flag struct { + User std.Address + Reason string +} + +func NewFlag(creator std.Address, reason string) Flag { + return Flag{ + User: creator, + Reason: reason, + } +} + +type Flaggable interface { + // AddFlag adds a new flag to an entry. + // + // Returns false if item was already flagged by user. + AddFlag(flag Flag) bool + + // FlagsCount returns number of times entry was flagged. + FlagsCount() int +} + +// flagItem adds a flag to a flaggable item (post, thread, etc). +// +// Returns whether flag count threshold is reached and item can be hidden. +// +// Panics if flag count threshold was already reached. +func flagItem(item Flaggable, flag Flag) bool { + if item.FlagsCount() >= flagThreshold { + panic("item flag count threshold exceeded: " + strconv.Itoa(flagThreshold)) + } + + if !item.AddFlag(flag) { + panic("item has been already flagged by a user") + } + + return item.FlagsCount() == flagThreshold +} diff --git a/examples/gno.land/r/demo/boards2/permission.gno b/examples/gno.land/r/demo/boards2/permission.gno index 0fc5dc9515d4..8a7f0f9a83ed 100644 --- a/examples/gno.land/r/demo/boards2/permission.gno +++ b/examples/gno.land/r/demo/boards2/permission.gno @@ -7,8 +7,10 @@ const ( PermissionThreadCreate = "thread:create" PermissionThreadEdit = "thread:edit" PermissionThreadDelete = "thread:delete" + PermissionThreadFlag = "thread:flag" PermissionThreadRepost = "thread:repost" PermissionReplyDelete = "reply:delete" + PermissionReplyFlag = "reply:flag" PermissionMemberInvite = "member:invite" PermissionMemberRemove = "member:remove" ) diff --git a/examples/gno.land/r/demo/boards2/post.gno b/examples/gno.land/r/demo/boards2/post.gno index 7364b985c845..27ca884c19fe 100644 --- a/examples/gno.land/r/demo/boards2/post.gno +++ b/examples/gno.land/r/demo/boards2/post.gno @@ -30,12 +30,14 @@ type Post struct { creator std.Address title string // optional body string + isHidden bool replies avl.Tree // Post.id -> *Post repliesAll avl.Tree // Post.id -> *Post (all replies, for top-level posts) reposts avl.Tree // Board.id -> Post.id - threadID PostID // original Post.id - parentID PostID // parent Post.id (if reply or repost) - repostBoardID BoardID // original Board.id (if repost) + flags []Flag + threadID PostID // original Post.id + parentID PostID // parent Post.id (if reply or repost) + repostBoardID BoardID // original Board.id (if repost) createdAt time.Time updatedAt time.Time } @@ -97,6 +99,30 @@ func (post *Post) GetUpdatedAt() time.Time { return post.updatedAt } +func (post *Post) AddFlag(flag Flag) bool { + // TODO: sort flags for fast search in case of big thresholds + for _, v := range post.flags { + if v.User == flag.User { + return false + } + } + + post.flags = append(post.flags, flag) + return true +} + +func (post *Post) FlagsCount() int { + return len(post.flags) +} + +func (post *Post) SetVisibility(isVisible bool) { + post.isHidden = !isVisible +} + +func (post *Post) Hidden() bool { + return post.isHidden +} + func (post *Post) AddReply(creator std.Address, body string) *Post { board := post.board pid := board.incGetPostID() diff --git a/examples/gno.land/r/demo/boards2/public.gno b/examples/gno.land/r/demo/boards2/public.gno index 9820c45499f0..5c88124af078 100644 --- a/examples/gno.land/r/demo/boards2/public.gno +++ b/examples/gno.land/r/demo/boards2/public.gno @@ -39,6 +39,21 @@ func CreateBoard(name string) BoardID { return id } +func FlagThread(bid BoardID, postID PostID, reason string) { + caller := std.GetOrigCaller() + assertHasPermission(caller, PermissionThreadFlag) + + board := mustGetBoard(bid) + t, ok := board.GetThread(postID) + if !ok { + panic("post doesn't exist") + } + + if flagItem(t, NewFlag(caller, reason)) { + t.SetVisibility(false) + } +} + func CreateThread(bid BoardID, title, body string) PostID { assertIsUserCall() @@ -75,6 +90,19 @@ func CreateReply(bid BoardID, threadID, replyID PostID, body string) PostID { return reply.id } +func FlagReply(bid BoardID, threadID, replyID PostID, reason string) { + caller := std.GetOrigCaller() + assertHasPermission(caller, PermissionThreadFlag) + + board := mustGetBoard(bid) + thread := mustGetThread(board, threadID) + reply := mustGetReply(thread, replyID) + + if flagItem(reply, NewFlag(caller, reason)) { + reply.SetVisibility(false) + } +} + func CreateRepost(bid BoardID, threadID PostID, title, body string, dstBoardID BoardID) PostID { assertIsUserCall()